diff --git a/arch/Makefile b/arch/Makefile new file mode 100644 index 0000000..81006f0 --- /dev/null +++ b/arch/Makefile @@ -0,0 +1,37 @@ +#################################################### +# Students' Makefile for the CS:APP Architecture Lab +#################################################### + +# Default team name and version number +TEAM = bovik +VERSION = 1 + +# Where are the different parts of the lab should be copied to when they +# are handed in. +HANDINDIR-PARTA = /afs/cs/academic/class/15349-s02/archlab/handin-parta +HANDINDIR-PARTB = /afs/cs/academic/class/15349-s02/archlab/handin-partb +HANDINDIR-PARTC = /afs/cs/academic/class/15349-s02/archlab/handin-partc + +sim: + (cd sim; make) + +# Use this rule to hand in Part A ("make handin-parta") +handin-parta: + cp sim/misc/sum.ys $(HANDINDIR-PARTA)/$(TEAM)-$(VERSION)-sum.ys + cp sim/misc/rsum.ys $(HANDINDIR-PARTA)/$(TEAM)-$(VERSION)-rsum.ys + cp sim/misc/copy.ys $(HANDINDIR-PARTA)/$(TEAM)-$(VERSION)-copy.ys + +# Use this rule to handin Part B ("make handin-partb") +handin-partb: + cp sim/seq/seq-full.hcl $(HANDINDIR-PARTB)/$(TEAM)-$(VERSION)-seq-full.hcl + +# Use this rule to handin Part C ("make handin-partc") +handin-partc: + cp sim/pipe/ncopy.ys $(HANDINDIR-PARTC)/$(TEAM)-$(VERSION)-ncopy.ys + cp sim/pipe/pipe-full.hcl $(HANDINDIR-PARTC)/$(TEAM)-$(VERSION)-pipe-full.hcl + +clean: + rm -f *~ *.o + + + diff --git a/arch/README b/arch/README new file mode 100644 index 0000000..6608a94 --- /dev/null +++ b/arch/README @@ -0,0 +1,22 @@ +##################################################################### +# CS:APP Architecture Lab +# Handout files for students +# +# Copyright (c) 2002, 2010, 2015, R. Bryant and D. O'Hallaron, +# All rights reserved. May not be used, modified, or copied +# without permission. +# +###################################################################### + +This directory contains the files that you need for the CS:APP +architecture lab. + +****** +Files: +****** + +Makefile Use this to handin your solutions +README This file +archlab.{ps,pdf} Lab writeup +sim.tar Archive of the Y86-64 tools in tar format +simguide.{ps,pdf} CS:APP Guide to Simulators document diff --git a/arch/archlab.pdf b/arch/archlab.pdf new file mode 100644 index 0000000..21c87d0 Binary files /dev/null and b/arch/archlab.pdf differ diff --git a/arch/sim/Makefile b/arch/sim/Makefile new file mode 100644 index 0000000..7fd8f06 --- /dev/null +++ b/arch/sim/Makefile @@ -0,0 +1,38 @@ +# Comment this out if you don't have Tcl/Tk on your system + +#GUIMODE=-DHAS_GUI + +# Modify the following line so that gcc can find the libtcl.so and +# libtk.so libraries on your system. You may need to use the -L option +# to tell gcc which directory to look in. Comment this out if you +# don't have Tcl/Tk. + +TKLIBS=-L/usr/lib -ltk -ltcl + +# Modify the following line so that gcc can find the tcl.h and tk.h +# header files on your system. Comment this out if you don't have +# Tcl/Tk. + +TKINC=-isystem /usr/include/tcl8.5 + +################################################## +# You shouldn't need to modify anything below here +################################################## + +# Use this rule (make all) to build the Y86-64 tools. The variables you've +# assigned to GUIMODE, TKLIBS, and TKINC will override the values that +# are currently assigned in seq/Makefile and pipe/Makefile. +all: + (cd misc; make all) + (cd pipe; make all GUIMODE=$(GUIMODE) TKLIBS="$(TKLIBS)" TKINC="$(TKINC)") + (cd seq; make all GUIMODE=$(GUIMODE) TKLIBS="$(TKLIBS)" TKINC="$(TKINC)") + (cd y86-code; make all) + +clean: + rm -f *~ core + (cd misc; make clean) + (cd pipe; make clean) + (cd seq; make clean) + (cd y86-code; make clean) + (cd ptest; make clean) + diff --git a/arch/sim/README b/arch/sim/README new file mode 100644 index 0000000..95f1561 --- /dev/null +++ b/arch/sim/README @@ -0,0 +1,97 @@ +/*********************************************************************** + * Y86-64 Tools (Student Distribution) + * + * Copyright (c) 2002, 2010, 2015, R. Bryant and D. O'Hallaron, + * All rights reserved. May not be used, modified, or copied + * without permission. + ***********************************************************************/ + +This directory contains the student distribution of the Y86-64 tools. It +is a proper subset of the master distribution, minus the solution +files found in the master distribution. + +yas Y86-64 assembler +yis Y86-64 instruction (ISA) simulator +hcl2c HCL to C translator +hcl2v HCL to Verilog translator +ssim SEQ simulator +ssim+ SEQ+ simulator +psim PIPE simulator + +************************* +1. Building the Y86-64 tools +************************* + +The Y86-64 simulators can be configured to support TTY and GUI +interfaces. A simulator running in TTY mode prints all information +about its run-time behavior on the terminal. It's harder to understand what's +going on, but useful for automated testing, and doesn't require any +special installation features. A simulator running in GUI mode uses a +fancy graphical user interface. Nice for visualizing and debugging, +but requires installation of Tcl/Tk on your system. + +To build the Y86-64 tools, perform the following steps: + +NOTE: If your instructor prepared this distribution for you, then you +can skip Step 1 and proceed directly to Step 2. The Makefile will +already have the proper values for GUIMODE, TKLIBS, and TKINC for your +system. + +Step 1. Decide whether you want the TTY or GUI form of the simulators, +and then modify ./Makefile in this directory accordingly. (The changes +you make to the variables in this Makefile will override the values +already assigned in the Makefiles in the seq and pipe directories.) + +Building the GUI simulators: If you have Tcl/Tk installed on your +system, then you can build the GUI form by initializing the GUIMODE, +TKLIBS, and TKINC variables, as appropriate for your system. (The +default values work for Linux systems.) + +Assigning GUIMODE=-DHAS_GUI causes the necessary GUI support code in +the simulator sources to be included. The TKLIBS variable tells gcc +where to look for the libtcl.so and libtk.so libraries. And the TKINC +variable tells gcc where to find the tcl.h and tk.h header files. + +Building the TTY simulators: If you don't have Tcl/Tk installed on +your system, then build the TTY form by commenting out all three of +these variables (GUIMODE, TKLIBS, and TKINC) in the Makefile. + +Step 2: Once you've modified the Makefile to build either the GUI or +TTY form, then you can construct the entire set of Y86-64 tools by typing + + unix> make clean; make + +******** +2. Files +******** + +Makefile + Builds the Y86-64 tools + +README + This file + +misc/ + Source files for the Y86-64 assembler yas, the Y86-64 instruction + simulator yis, and the isa.c file that is used by the -t option + of the processor simulators to check the results against the + ISA simulation. Also contains files for the programs + hcl2c and hcl2v + +seq/ + Code for the SEQ and SEQ+ simulators. Contains HCL files for + labs and homework problems that involve modifying SEQ. + +pipe/ + Code for the PIPE simulator. Contains HCL files for labs and + homework problems that involve modifying PIPE. + +y86-code/ + Example .ys files from CS:APP and scripts for conducting + automated benchmark teseting of the new processor designs. + +ptest/ + Automated regression testing scripts for testing processor designs. + +verilog/ + System for producing Verilog designs from HCL code diff --git a/arch/sim/misc/Makefile b/arch/sim/misc/Makefile new file mode 100644 index 0000000..e2cc86c --- /dev/null +++ b/arch/sim/misc/Makefile @@ -0,0 +1,59 @@ +CC=gcc +CFLAGS=-Wall -O1 -g -fcommon +LCFLAGS=-O1 -fcommon +LEX = flex +YACC=bison +LEXLIB = -lfl +YAS=./yas + +all: yis yas hcl2c + +# These are implicit rules for making .yo files from .ys files. +# E.g., make sum.yo +.SUFFIXES: .ys .yo +.ys.yo: + $(YAS) $*.ys + +# These are the explicit rules for making yis yas and hcl2c and hcl2v +yas-grammar.o: yas-grammar.c + $(CC) $(LCFLAGS) -c yas-grammar.c + +yas-grammar.c: yas-grammar.lex + $(LEX) yas-grammar.lex + mv lex.yy.c yas-grammar.c + +isa.o: isa.c isa.h + $(CC) $(CFLAGS) -c isa.c + +yas.o: yas.c yas.h isa.h + $(CC) $(CFLAGS) -c yas.c + +yas: yas.o yas-grammar.o isa.o + $(CC) $(CFLAGS) yas-grammar.o yas.o isa.o ${LEXLIB} -o yas + +yis.o: yis.c isa.h + $(CC) $(CFLAGS) -c yis.c + +yis: yis.o isa.o + $(CC) $(CFLAGS) yis.o isa.o -o yis + +hcl2c: hcl.tab.c lex.yy.c node.c outgen.c + $(CC) $(LCFLAGS) node.c lex.yy.c hcl.tab.c outgen.c -o hcl2c + +hcl2v: hcl.tab.c lex.yy.c node.c outgen.c + $(CC) $(LCFLAGS) -DVLOG node.c lex.yy.c hcl.tab.c outgen.c -o hcl2v + +hcl2u: hcl.tab.c lex.yy.c node.c outgen.c + $(CC) $(LCFLAGS) -DUCLID node.c lex.yy.c hcl.tab.c outgen.c -o hcl2u + +lex.yy.c: hcl.lex + $(LEX) hcl.lex + +hcl.tab.c: hcl.y + $(YACC) -d hcl.y + +clean: + rm -f *.o *.yo *.exe yis yas hcl2c mux4 *~ core.* + rm -f hcl.tab.c hcl.tab.h lex.yy.c yas-grammar.c + + diff --git a/arch/sim/misc/README b/arch/sim/misc/README new file mode 100644 index 0000000..e1cbdd5 --- /dev/null +++ b/arch/sim/misc/README @@ -0,0 +1,71 @@ +/*********************************************************************** + * Y86-64 Assembler, Instruction Simulator, and HCL translator + * + * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + ***********************************************************************/ + +This directory contains all of the source files for the following: + +YAS Y86-64 assembler +YIS Y86-64 instruction level simulator +HCL2C HCL to C translator +HCL2V HCL to Verilog translator + +********************* +1. Building the tools +********************* + +unix> make clean +unix> make + +******** +2. Files +******** + +Makefile Builds yas, yis, hcl2c, hcl2v +README This file + +* Versions of Makefile in the student's distribution +* (Instructor distribution only) +Makefile-sim + +* Example programs for Part A of the CS:APP Architecture Lab +examples.c C versions of three Y86-64 functions +ans-copy.ys Solution copy function (instructor distribution only) +ans-sum.ys Solution sum function (instructor distribution only) +ans-rsum.ys Solution rsum function (instructor distribution only) + + +* Instruction simulator code shared by yas, yis, ssim, ssim+, and psim +isa.c +isa.h + +* Files used to build the yas assembler +yas The YAS binary +yas.c yas source file and header file +yas.h +yas-grammar.lex Y86-64 lexical scanner spec +yas-grammar.c Lexical scanner generated from yas-grammar.lex + +* Files used to build the yis instruction simulator +yis The YIS binary +yis.c yis source file + +* Files used to build the hcl2c translator +hcl2c The HCL2C binary +node.c auxiliary routines and header file +node.h +hcl.lex HCL lexical scanner spec +lex.yy.c HCL lexical scanner generated from hcl.lex +hcl.y HCL grammar +hcl.tab.c HCL parser generated from hcl.y +hcl.tab.h Token definitions + +* Example HCL programs used during the writing of the CS:APP book +* (Instructor distribution only) +frag.{hcl,c} +mux4.{hcl,c} +reg-file.{hcl,c} + + diff --git a/arch/sim/misc/copy.ys b/arch/sim/misc/copy.ys new file mode 100644 index 0000000..f433453 --- /dev/null +++ b/arch/sim/misc/copy.ys @@ -0,0 +1,44 @@ + .pos 0 + irmovq stack, %rsp + call main + halt + + .align 8 +# Source block +src: + .quad 0x00a + .quad 0x0b0 + .quad 0xc00 + +# Destination block +dest: + .quad 0x111 + .quad 0x222 + .quad 0x333 + +main: + irmovq src, %rdi + irmovq dest, %rsi + irmovq $3, %rdx + call copy + ret + +copy: + xorq %rax, %rax + irmovq $1, %rcx + irmovq $8, %r8 + jmp test +loop: + mrmovq (%rdi), %r9 + rmmovq %r9, (%rsi) + xorq %r9, %rax + addq %r8, %rdi + addq %r8, %rsi + subq %rcx, %rdx +test: + andq %rdx, %rdx + jg loop + ret + + .pos 0x200 +stack: diff --git a/arch/sim/misc/examples.c b/arch/sim/misc/examples.c new file mode 100644 index 0000000..6f09d43 --- /dev/null +++ b/arch/sim/misc/examples.c @@ -0,0 +1,50 @@ +/* + * Architecture Lab: Part A + * + * High level specs for the functions that the students will rewrite + * in Y86-64 assembly language + */ + +/* $begin examples */ +/* linked list element */ +typedef struct ELE { + long val; + struct ELE *next; +} *list_ptr; + +/* sum_list - Sum the elements of a linked list */ +long sum_list(list_ptr ls) +{ + long val = 0; + while (ls) { + val += ls->val; + ls = ls->next; + } + return val; +} + +/* rsum_list - Recursive version of sum_list */ +long rsum_list(list_ptr ls) +{ + if (!ls) + return 0; + else { + long val = ls->val; + long rest = rsum_list(ls->next); + return val + rest; + } +} + +/* copy_block - Copy src to dest and return xor checksum of src */ +long copy_block(long *src, long *dest, long len) +{ + long result = 0; + while (len > 0) { + long val = *src++; + *dest++ = val; + result ^= val; + len--; + } + return result; +} +/* $end examples */ diff --git a/arch/sim/misc/hcl.lex b/arch/sim/misc/hcl.lex new file mode 100644 index 0000000..f2ca313 --- /dev/null +++ b/arch/sim/misc/hcl.lex @@ -0,0 +1,45 @@ +%{ +#include +#include "node.h" +#define YYSTYPE node_ptr +#include "hcl.tab.h" + + +extern YYSTYPE yylval; +extern int lineno; +%} +%% +[ \r\t\f] ; +[\n] lineno++; +"#".*\n lineno++ ; +quote return(QUOTE); +boolsig return(BOOLARG); +bool return(BOOL); +wordsig return(WORDARG); +word return(WORD); +in return(IN); +'[^']*' yylval = make_quote(yytext); return(QSTRING); +[a-zA-Z][a-zA-Z0-9_]* yylval = make_var(yytext); return(VAR); +[0-9][0-9]* yylval = make_num(yytext); return(NUM); +-[0-9][0-9]* yylval = make_num(yytext); return(NUM); +"=" return(ASSIGN); +";" return(SEMI); +":" return(COLON); +"," return(COMMA); +"(" return(LPAREN); +")" return(RPAREN); +"{" return(LBRACE); +"}" return(RBRACE); +"[" return(LBRACK); +"]" return(RBRACK); +"&&" return(AND); +"||" return(OR); +"!=" yylval = make_var(yytext); return(COMP); +"==" yylval = make_var(yytext); return(COMP); +"<" yylval = make_var(yytext); return(COMP); +"<=" yylval = make_var(yytext); return(COMP); +">" yylval = make_var(yytext); return(COMP); +">=" yylval = make_var(yytext); return(COMP); +"!" return(NOT); +%% + diff --git a/arch/sim/misc/hcl.y b/arch/sim/misc/hcl.y new file mode 100644 index 0000000..269a2f6 --- /dev/null +++ b/arch/sim/misc/hcl.y @@ -0,0 +1,96 @@ +%{ +#include +#include +#include +#include "node.h" +#define YYSTYPE node_ptr + +/* Current line number. Maintained by lex */ +int lineno = 1; +#define ERRLIM 5 +int errcnt = 0; + + + +FILE *outfile; + +int yyparse(void); +int yylex(void); + +void yyerror(const char *str) +{ + fprintf(stderr, "Error, near line %d: %s\n", lineno, str); + if (++errcnt > ERRLIM) { + fprintf(stderr, "Too many errors, aborting\n"); + exit(1); + } +} + +static char errmsg[1024]; +void yyserror(const char *str, char *other) +{ + sprintf(errmsg, str, other); + yyerror(errmsg); +} + +int yywrap() +{ + return 1; +} + +int main(int argc, char **argv) +{ + init_node(argc, argv); + outfile = stdout; + yyparse(); + finish_node(0); + return errcnt != 0; +} + +%} + +%token QUOTE BOOLARG BOOL WORDARG WORD QSTRING + VAR NUM ASSIGN SEMI COLON COMMA LPAREN RPAREN LBRACE + RBRACE LBRACK RBRACK AND OR NOT COMP IN + +/* All operators are left associative. Listed from lowest to highest */ +%left OR +%left AND +%left NOT +%left COMP +%left IN + +%% + +statements: /* empty */ + | statements statement + ; + +statement: + QUOTE QSTRING { insert_code($2); } + | BOOLARG VAR QSTRING { add_arg($2, $3, 1); } + | WORDARG VAR QSTRING { add_arg($2, $3, 0); } + | BOOL VAR ASSIGN expr SEMI { gen_funct($2, $4, 1); } + | WORD VAR ASSIGN expr SEMI { gen_funct($2, $4, 0); } + ; + +expr: + VAR { $$=$1; } + | NUM { $$=$1; } + | LPAREN expr RPAREN { $$=$2; } + | NOT expr { $$=make_not($2); } + | expr AND expr { $$=make_and($1, $3); } + | expr OR expr { $$=make_or($1, $3); } + | expr COMP expr { $$=make_comp($2,$1,$3); } + | expr IN LBRACE exprlist RBRACE { $$=make_ele($1, $4);} + | LBRACK caselist RBRACK { $$=$2; } + ; + +exprlist: + expr { $$=$1; } + | exprlist COMMA expr { $$=concat($1, $3); } + +caselist: + /* Empty */ { $$=NULL; } + | caselist expr COLON expr SEMI { $$=concat($1, make_case($2, $4));} + diff --git a/arch/sim/misc/isa.c b/arch/sim/misc/isa.c new file mode 100644 index 0000000..484e422 --- /dev/null +++ b/arch/sim/misc/isa.c @@ -0,0 +1,943 @@ +#include +#include +#include +#include +#include "isa.h" + + +/* Are we running in GUI mode? */ +extern int gui_mode; + +/* Bytes Per Line = Block size of memory */ +#define BPL 32 + +struct { + char *name; + int id; +} reg_table[REG_ERR+1] = +{ + {"%rax", REG_RAX}, + {"%rcx", REG_RCX}, + {"%rdx", REG_RDX}, + {"%rbx", REG_RBX}, + {"%rsp", REG_RSP}, + {"%rbp", REG_RBP}, + {"%rsi", REG_RSI}, + {"%rdi", REG_RDI}, + {"%r8", REG_R8}, + {"%r9", REG_R9}, + {"%r10", REG_R10}, + {"%r11", REG_R11}, + {"%r12", REG_R12}, + {"%r13", REG_R13}, + {"%r14", REG_R14}, + {"----", REG_NONE}, + {"----", REG_ERR} +}; + + +reg_id_t find_register(char *name) +{ + int i; + for (i = 0; i < REG_NONE; i++) + if (!strcmp(name, reg_table[i].name)) + return reg_table[i].id; + return REG_ERR; +} + +char *reg_name(reg_id_t id) +{ + if (id >= 0 && id < REG_NONE) + return reg_table[id].name; + else + return reg_table[REG_NONE].name; +} + +/* Is the given register ID a valid program register? */ +int reg_valid(reg_id_t id) +{ + return id >= 0 && id < REG_NONE && reg_table[id].id == id; +} + +instr_t instruction_set[] = +{ + {"nop", HPACK(I_NOP, F_NONE), 1, NO_ARG, 0, 0, NO_ARG, 0, 0 }, + {"halt", HPACK(I_HALT, F_NONE), 1, NO_ARG, 0, 0, NO_ARG, 0, 0 }, + {"rrmovq", HPACK(I_RRMOVQ, F_NONE), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + /* Conditional move instructions are variants of RRMOVQ */ + {"cmovle", HPACK(I_RRMOVQ, C_LE), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + {"cmovl", HPACK(I_RRMOVQ, C_L), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + {"cmove", HPACK(I_RRMOVQ, C_E), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + {"cmovne", HPACK(I_RRMOVQ, C_NE), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + {"cmovge", HPACK(I_RRMOVQ, C_GE), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + {"cmovg", HPACK(I_RRMOVQ, C_G), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + /* arg1hi indicates number of bytes */ + {"irmovq", HPACK(I_IRMOVQ, F_NONE), 10, I_ARG, 2, 8, R_ARG, 1, 0 }, + {"rmmovq", HPACK(I_RMMOVQ, F_NONE), 10, R_ARG, 1, 1, M_ARG, 1, 0 }, + {"mrmovq", HPACK(I_MRMOVQ, F_NONE), 10, M_ARG, 1, 0, R_ARG, 1, 1 }, + {"addq", HPACK(I_ALU, A_ADD), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + {"subq", HPACK(I_ALU, A_SUB), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + {"andq", HPACK(I_ALU, A_AND), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + {"xorq", HPACK(I_ALU, A_XOR), 2, R_ARG, 1, 1, R_ARG, 1, 0 }, + /* arg1hi indicates number of bytes */ + {"jmp", HPACK(I_JMP, C_YES), 9, I_ARG, 1, 8, NO_ARG, 0, 0 }, + {"jle", HPACK(I_JMP, C_LE), 9, I_ARG, 1, 8, NO_ARG, 0, 0 }, + {"jl", HPACK(I_JMP, C_L), 9, I_ARG, 1, 8, NO_ARG, 0, 0 }, + {"je", HPACK(I_JMP, C_E), 9, I_ARG, 1, 8, NO_ARG, 0, 0 }, + {"jne", HPACK(I_JMP, C_NE), 9, I_ARG, 1, 8, NO_ARG, 0, 0 }, + {"jge", HPACK(I_JMP, C_GE), 9, I_ARG, 1, 8, NO_ARG, 0, 0 }, + {"jg", HPACK(I_JMP, C_G), 9, I_ARG, 1, 8, NO_ARG, 0, 0 }, + {"call", HPACK(I_CALL, F_NONE), 9, I_ARG, 1, 8, NO_ARG, 0, 0 }, + {"ret", HPACK(I_RET, F_NONE), 1, NO_ARG, 0, 0, NO_ARG, 0, 0 }, + {"pushq", HPACK(I_PUSHQ, F_NONE) , 2, R_ARG, 1, 1, NO_ARG, 0, 0 }, + {"popq", HPACK(I_POPQ, F_NONE) , 2, R_ARG, 1, 1, NO_ARG, 0, 0 }, + {"iaddq", HPACK(I_IADDQ, F_NONE), 10, I_ARG, 2, 8, R_ARG, 1, 0 }, + /* this is just a hack to make the I_POP2 code have an associated name */ + {"pop2", HPACK(I_POP2, F_NONE) , 0, NO_ARG, 0, 0, NO_ARG, 0, 0 }, + + /* For allocation instructions, arg1hi indicates number of bytes */ + {".byte", 0x00, 1, I_ARG, 0, 1, NO_ARG, 0, 0 }, + {".word", 0x00, 2, I_ARG, 0, 2, NO_ARG, 0, 0 }, + {".long", 0x00, 4, I_ARG, 0, 4, NO_ARG, 0, 0 }, + {".quad", 0x00, 8, I_ARG, 0, 8, NO_ARG, 0, 0 }, + {NULL, 0 , 0, NO_ARG, 0, 0, NO_ARG, 0, 0 } +}; + +instr_t invalid_instr = + {"XXX", 0 , 0, NO_ARG, 0, 0, NO_ARG, 0, 0 }; + +instr_ptr find_instr(char *name) +{ + int i; + for (i = 0; instruction_set[i].name; i++) + if (strcmp(instruction_set[i].name,name) == 0) + return &instruction_set[i]; + return NULL; +} + +/* Return name of instruction given its encoding */ +char *iname(int instr) { + int i; + for (i = 0; instruction_set[i].name; i++) { + if (instr == instruction_set[i].code) + return instruction_set[i].name; + } + return ""; +} + + +instr_ptr bad_instr() +{ + return &invalid_instr; +} + + +mem_t init_mem(int len) +{ + + mem_t result = (mem_t) malloc(sizeof(mem_rec)); + len = ((len+BPL-1)/BPL)*BPL; + result->len = len; + result->contents = (byte_t *) calloc(len, 1); + return result; +} + +void clear_mem(mem_t m) +{ + memset(m->contents, 0, m->len); +} + +void free_mem(mem_t m) +{ + free((void *) m->contents); + free((void *) m); +} + +mem_t copy_mem(mem_t oldm) +{ + mem_t newm = init_mem(oldm->len); + memcpy(newm->contents, oldm->contents, oldm->len); + return newm; +} + +bool_t diff_mem(mem_t oldm, mem_t newm, FILE *outfile) +{ + word_t pos; + int len = oldm->len; + bool_t diff = FALSE; + if (newm->len < len) + len = newm->len; + for (pos = 0; (!diff || outfile) && pos < len; pos += 8) { + word_t ov = 0; word_t nv = 0; + get_word_val(oldm, pos, &ov); + get_word_val(newm, pos, &nv); + if (nv != ov) { + diff = TRUE; + if (outfile) + fprintf(outfile, "0x%.4llx:\t0x%.16llx\t0x%.16llx\n", pos, ov, nv); + } + } + return diff; +} + +int hex2dig(char c) +{ + if (isdigit((int)c)) + return c - '0'; + if (isupper((int)c)) + return c - 'A' + 10; + else + return c - 'a' + 10; +} + +#define LINELEN 4096 +int load_mem(mem_t m, FILE *infile, int report_error) +{ + /* Read contents of .yo file */ + char buf[LINELEN]; + char c, ch, cl; + int byte_cnt = 0; + int lineno = 0; + word_t bytepos = 0; +#ifdef HAS_GUI + int empty_line = 1; + int addr = 0; + char hexcode[21]; + /* For display */ + int line_no = 0; + char line[LINELEN]; + int index = 0; +#endif /* HAS_GUI */ + while (fgets(buf, LINELEN, infile)) { + int cpos = 0; +#ifdef HAS_GUI + empty_line = 1; +#endif + lineno++; + /* Skip white space */ + while (isspace((int)buf[cpos])) + cpos++; + + if (buf[cpos] != '0' || + (buf[cpos+1] != 'x' && buf[cpos+1] != 'X')) + continue; /* Skip this line */ + cpos+=2; + + /* Get address */ + bytepos = 0; + while (isxdigit((int)(c=buf[cpos]))) { + cpos++; + bytepos = bytepos*16 + hex2dig(c); + } + + while (isspace((int)buf[cpos])) + cpos++; + + if (buf[cpos++] != ':') { + if (report_error) { + fprintf(stderr, "Error reading file. Expected colon\n"); + fprintf(stderr, "Line %d:%s\n", lineno, buf); + fprintf(stderr, + "Reading '%c' at position %d\n", buf[cpos], cpos); + } + return 0; + } + +#ifdef HAS_GUI + addr = bytepos; + index = 0; +#endif + + while (isspace((int)buf[cpos])) + cpos++; + + /* Get code */ + while (isxdigit((int)(ch=buf[cpos++])) && + isxdigit((int)(cl=buf[cpos++]))) { + byte_t byte = 0; + if (bytepos >= m->len) { + if (report_error) { + fprintf(stderr, + "Error reading file. Invalid address. 0x%llx\n", + bytepos); + fprintf(stderr, "Line %d:%s\n", lineno, buf); + } + return 0; + } + byte = hex2dig(ch)*16+hex2dig(cl); + m->contents[bytepos++] = byte; + byte_cnt++; +#ifdef HAS_GUI + empty_line = 0; + hexcode[index++] = ch; + hexcode[index++] = cl; +#endif + } +#ifdef HAS_GUI + /* Fill rest of hexcode with blanks. + Needs to be 2x longest instruction */ + for (; index < 20; index++) + hexcode[index] = ' '; + hexcode[index] = '\0'; + + if (gui_mode) { + /* Now get the rest of the line */ + while (isspace((int)buf[cpos])) + cpos++; + cpos++; /* Skip over '|' */ + + index = 0; + while ((c = buf[cpos++]) != '\0' && c != '\n') { + line[index++] = c; + } + line[index] = '\0'; + if (!empty_line) + report_line(line_no++, addr, hexcode, line); + } +#endif /* HAS_GUI */ + } + return byte_cnt; +} + +bool_t get_byte_val(mem_t m, word_t pos, byte_t *dest) +{ + if (pos < 0 || pos >= m->len) + return FALSE; + *dest = m->contents[pos]; + return TRUE; +} + +bool_t get_word_val(mem_t m, word_t pos, word_t *dest) +{ + int i; + word_t val; + if (pos < 0 || pos + 8 > m->len) + return FALSE; + val = 0; + for (i = 0; i < 8; i++) { + word_t b = m->contents[pos+i] & 0xFF; + val = val | (b <<(8*i)); + } + *dest = val; + return TRUE; +} + +bool_t set_byte_val(mem_t m, word_t pos, byte_t val) +{ + if (pos < 0 || pos >= m->len) + return FALSE; + m->contents[pos] = val; + return TRUE; +} + +bool_t set_word_val(mem_t m, word_t pos, word_t val) +{ + int i; + if (pos < 0 || pos + 8 > m->len) + return FALSE; + for (i = 0; i < 8; i++) { + m->contents[pos+i] = (byte_t) val & 0xFF; + val >>= 8; + } + return TRUE; +} + +void dump_memory(FILE *outfile, mem_t m, word_t pos, int len) +{ + int i, j; + while (pos % BPL) { + pos --; + len ++; + } + + len = ((len+BPL-1)/BPL)*BPL; + + if (pos+len > m->len) + len = m->len-pos; + + for (i = 0; i < len; i+=BPL) { + word_t val = 0; + fprintf(outfile, "0x%.4llx:", pos+i); + for (j = 0; j < BPL; j+= 8) { + get_word_val(m, pos+i+j, &val); + fprintf(outfile, " %.16llx", val); + } + } +} + +mem_t init_reg() +{ + return init_mem(128); +} + +void free_reg(mem_t r) +{ + free_mem(r); +} + +mem_t copy_reg(mem_t oldr) +{ + return copy_mem(oldr); +} + +bool_t diff_reg(mem_t oldr, mem_t newr, FILE *outfile) +{ + word_t pos; + int len = oldr->len; + bool_t diff = FALSE; + if (newr->len < len) + len = newr->len; + for (pos = 0; (!diff || outfile) && pos < len; pos += 8) { + word_t ov = 0; + word_t nv = 0; + get_word_val(oldr, pos, &ov); + get_word_val(newr, pos, &nv); + if (nv != ov) { + diff = TRUE; + if (outfile) + fprintf(outfile, "%s:\t0x%.16llx\t0x%.16llx\n", + reg_table[pos/8].name, ov, nv); + } + } + return diff; +} + +word_t get_reg_val(mem_t r, reg_id_t id) +{ + word_t val = 0; + if (id >= REG_NONE) + return 0; + get_word_val(r,id*8, &val); + return val; +} + +void set_reg_val(mem_t r, reg_id_t id, word_t val) +{ + if (id < REG_NONE) { + set_word_val(r,id*8,val); +#ifdef HAS_GUI + if (gui_mode) { + signal_register_update(id, val); + } +#endif /* HAS_GUI */ + } +} + +void dump_reg(FILE *outfile, mem_t r) { + reg_id_t id; + for (id = 0; reg_valid(id); id++) { + fprintf(outfile, " %s ", reg_table[id].name); + } + fprintf(outfile, "\n"); + for (id = 0; reg_valid(id); id++) { + word_t val = 0; + get_word_val(r, id*8, &val); + fprintf(outfile, " %llx", val); + } + fprintf(outfile, "\n"); +} + +struct { + char symbol; + int id; +} alu_table[A_NONE+1] = +{ + {'+', A_ADD}, + {'-', A_SUB}, + {'&', A_AND}, + {'^', A_XOR}, + {'?', A_NONE} +}; + +char op_name(alu_t op) +{ + if (op < A_NONE) + return alu_table[op].symbol; + else + return alu_table[A_NONE].symbol; +} + +word_t compute_alu(alu_t op, word_t argA, word_t argB) +{ + word_t val; + switch(op) { + case A_ADD: + val = argA+argB; + break; + case A_SUB: + val = argB-argA; + break; + case A_AND: + val = argA&argB; + break; + case A_XOR: + val = argA^argB; + break; + default: + val = 0; + } + return val; +} + +cc_t compute_cc(alu_t op, word_t argA, word_t argB) +{ + word_t val = compute_alu(op, argA, argB); + bool_t zero = (val == 0); + bool_t sign = ((word_t)val < 0); + bool_t ovf; + switch(op) { + case A_ADD: + ovf = (((word_t) argA < 0) == ((word_t) argB < 0)) && + (((word_t) val < 0) != ((word_t) argA < 0)); + break; + case A_SUB: + ovf = (((word_t) argA > 0) == ((word_t) argB < 0)) && + (((word_t) val < 0) != ((word_t) argB < 0)); + break; + case A_AND: + case A_XOR: + ovf = FALSE; + break; + default: + ovf = FALSE; + } + return PACK_CC(zero,sign,ovf); + +} + +char *cc_names[8] = { + "Z=0 S=0 O=0", + "Z=0 S=0 O=1", + "Z=0 S=1 O=0", + "Z=0 S=1 O=1", + "Z=1 S=0 O=0", + "Z=1 S=0 O=1", + "Z=1 S=1 O=0", + "Z=1 S=1 O=1"}; + +char *cc_name(cc_t c) +{ + int ci = c; + if (ci < 0 || ci > 7) + return "???????????"; + else + return cc_names[c]; +} + +/* Status types */ + +char *stat_names[] = { "BUB", "AOK", "HLT", "ADR", "INS", "PIP" }; + +char *stat_name(stat_t e) +{ + if (e < 0 || e > STAT_PIP) + return "Invalid Status"; + return stat_names[e]; +} + +/**************** Implementation of ISA model ************************/ + +state_ptr new_state(int memlen) +{ + state_ptr result = (state_ptr) malloc(sizeof(state_rec)); + result->pc = 0; + result->r = init_reg(); + result->m = init_mem(memlen); + result->cc = DEFAULT_CC; + return result; +} + +void free_state(state_ptr s) +{ + free_reg(s->r); + free_mem(s->m); + free((void *) s); +} + +state_ptr copy_state(state_ptr s) { + state_ptr result = (state_ptr) malloc(sizeof(state_rec)); + result->pc = s->pc; + result->r = copy_reg(s->r); + result->m = copy_mem(s->m); + result->cc = s->cc; + return result; +} + +bool_t diff_state(state_ptr olds, state_ptr news, FILE *outfile) { + bool_t diff = FALSE; + + if (olds->pc != news->pc) { + diff = TRUE; + if (outfile) { + fprintf(outfile, "pc:\t0x%.16llx\t0x%.16llx\n", olds->pc, news->pc); + } + } + if (olds->cc != news->cc) { + diff = TRUE; + if (outfile) { + fprintf(outfile, "cc:\t%s\t%s\n", cc_name(olds->cc), cc_name(news->cc)); + } + } + if (diff_reg(olds->r, news->r, outfile)) + diff = TRUE; + if (diff_mem(olds->m, news->m, outfile)) + diff = TRUE; + return diff; +} + + +/* Branch logic */ +bool_t cond_holds(cc_t cc, cond_t bcond) { + bool_t zf = GET_ZF(cc); + bool_t sf = GET_SF(cc); + bool_t of = GET_OF(cc); + bool_t jump = FALSE; + + switch(bcond) { + case C_YES: + jump = TRUE; + break; + case C_LE: + jump = (sf^of)|zf; + break; + case C_L: + jump = sf^of; + break; + case C_E: + jump = zf; + break; + case C_NE: + jump = zf^1; + break; + case C_GE: + jump = sf^of^1; + break; + case C_G: + jump = (sf^of^1)&(zf^1); + break; + default: + jump = FALSE; + break; + } + return jump; +} + + +/* Execute single instruction. Return status. */ +stat_t step_state(state_ptr s, FILE *error_file) +{ + word_t argA, argB; + byte_t byte0 = 0; + byte_t byte1 = 0; + itype_t hi0; + alu_t lo0; + reg_id_t hi1 = REG_NONE; + reg_id_t lo1 = REG_NONE; + bool_t ok1 = TRUE; + word_t cval = 0; + word_t okc = TRUE; + word_t val, dval; + bool_t need_regids; + bool_t need_imm; + word_t ftpc = s->pc; /* Fall-through PC */ + + if (!get_byte_val(s->m, ftpc, &byte0)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + ftpc++; + + hi0 = HI4(byte0); + lo0 = LO4(byte0); + + need_regids = + (hi0 == I_RRMOVQ || hi0 == I_ALU || hi0 == I_PUSHQ || + hi0 == I_POPQ || hi0 == I_IRMOVQ || hi0 == I_RMMOVQ || + hi0 == I_MRMOVQ || hi0 == I_IADDQ); + + if (need_regids) { + ok1 = get_byte_val(s->m, ftpc, &byte1); + ftpc++; + hi1 = HI4(byte1); + lo1 = LO4(byte1); + } + + need_imm = + (hi0 == I_IRMOVQ || hi0 == I_RMMOVQ || hi0 == I_MRMOVQ || + hi0 == I_JMP || hi0 == I_CALL || hi0 == I_IADDQ); + + if (need_imm) { + okc = get_word_val(s->m, ftpc, &cval); + ftpc += 8; + } + + switch (hi0) { + case I_NOP: + s->pc = ftpc; + break; + case I_HALT: + return STAT_HLT; + break; + case I_RRMOVQ: /* Both unconditional and conditional moves */ + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!reg_valid(hi1)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid register ID 0x%.1x\n", + s->pc, hi1); + return STAT_INS; + } + if (!reg_valid(lo1)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid register ID 0x%.1x\n", + s->pc, lo1); + return STAT_INS; + } + val = get_reg_val(s->r, hi1); + if (cond_holds(s->cc, lo0)) + set_reg_val(s->r, lo1, val); + s->pc = ftpc; + break; + case I_IRMOVQ: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!okc) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address", + s->pc); + return STAT_INS; + } + if (!reg_valid(lo1)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid register ID 0x%.1x\n", + s->pc, lo1); + return STAT_INS; + } + set_reg_val(s->r, lo1, cval); + s->pc = ftpc; + break; + case I_RMMOVQ: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!okc) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_INS; + } + if (!reg_valid(hi1)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid register ID 0x%.1x\n", + s->pc, hi1); + return STAT_INS; + } + if (reg_valid(lo1)) + cval += get_reg_val(s->r, lo1); + val = get_reg_val(s->r, hi1); + if (!set_word_val(s->m, cval, val)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid data address 0x%llx\n", + s->pc, cval); + return STAT_ADR; + } + s->pc = ftpc; + break; + case I_MRMOVQ: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!okc) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction addres\n", s->pc); + return STAT_INS; + } + if (!reg_valid(hi1)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid register ID 0x%.1x\n", + s->pc, hi1); + return STAT_INS; + } + if (reg_valid(lo1)) + cval += get_reg_val(s->r, lo1); + if (!get_word_val(s->m, cval, &val)) + return STAT_ADR; + set_reg_val(s->r, hi1, val); + s->pc = ftpc; + break; + case I_ALU: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + argA = get_reg_val(s->r, hi1); + argB = get_reg_val(s->r, lo1); + val = compute_alu(lo0, argA, argB); + set_reg_val(s->r, lo1, val); + s->cc = compute_cc(lo0, argA, argB); + s->pc = ftpc; + break; + case I_JMP: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!okc) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (cond_holds(s->cc, lo0)) + s->pc = cval; + else + s->pc = ftpc; + break; + case I_CALL: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!okc) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + val = get_reg_val(s->r, REG_RSP) - 8; + set_reg_val(s->r, REG_RSP, val); + if (!set_word_val(s->m, val, ftpc)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid stack address 0x%llx\n", s->pc, val); + return STAT_ADR; + } + s->pc = cval; + break; + case I_RET: + /* Return Instruction. Pop address from stack */ + dval = get_reg_val(s->r, REG_RSP); + if (!get_word_val(s->m, dval, &val)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid stack address 0x%llx\n", + s->pc, dval); + return STAT_ADR; + } + set_reg_val(s->r, REG_RSP, dval + 8); + s->pc = val; + break; + case I_PUSHQ: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!reg_valid(hi1)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid register ID 0x%.1x\n", s->pc, hi1); + return STAT_INS; + } + val = get_reg_val(s->r, hi1); + dval = get_reg_val(s->r, REG_RSP) - 8; + set_reg_val(s->r, REG_RSP, dval); + if (!set_word_val(s->m, dval, val)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid stack address 0x%llx\n", s->pc, dval); + return STAT_ADR; + } + s->pc = ftpc; + break; + case I_POPQ: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!reg_valid(hi1)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid register ID 0x%.1x\n", s->pc, hi1); + return STAT_INS; + } + dval = get_reg_val(s->r, REG_RSP); + set_reg_val(s->r, REG_RSP, dval+8); + if (!get_word_val(s->m, dval, &val)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid stack address 0x%llx\n", + s->pc, dval); + return STAT_ADR; + } + set_reg_val(s->r, hi1, val); + s->pc = ftpc; + break; + case I_IADDQ: + if (!ok1) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address\n", s->pc); + return STAT_ADR; + } + if (!okc) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction address", + s->pc); + return STAT_INS; + } + if (!reg_valid(lo1)) { + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid register ID 0x%.1x\n", + s->pc, lo1); + return STAT_INS; + } + argB = get_reg_val(s->r, lo1); + val = argB + cval; + set_reg_val(s->r, lo1, val); + s->cc = compute_cc(A_ADD, cval, argB); + s->pc = ftpc; + break; + default: + if (error_file) + fprintf(error_file, + "PC = 0x%llx, Invalid instruction %.2x\n", s->pc, byte0); + return STAT_INS; + } + return STAT_AOK; +} diff --git a/arch/sim/misc/isa.h b/arch/sim/misc/isa.h new file mode 100644 index 0000000..3c8adb9 --- /dev/null +++ b/arch/sim/misc/isa.h @@ -0,0 +1,210 @@ +/* Instruction Set definition for Y86-64 Architecture */ +/* Revisions: + 2013-10-25: + Extended all data widths and addresses to 64 bits + Changed all 'l' instructions to 'q' + Changed registers name from e-form to r-form + Added registers %r8 to %r14 + 2009-03-11: + Changed RNONE to be 0xF + Changed J_XX and jump_t to C_XX and cond_t; take_branch to cond_holds + Expanded RRMOVL to include conditional moves +*/ + +/**************** Registers *************************/ + +/* REG_NONE is a special one to indicate no register */ +typedef enum { REG_RAX, REG_RCX, REG_RDX, REG_RBX, + REG_RSP, REG_RBP, REG_RSI, REG_RDI, + REG_R8, REG_R9, REG_R10, REG_R11, + REG_R12, REG_R13, REG_R14, REG_NONE=0xF, REG_ERR } reg_id_t; + +/* Find register ID given its name */ +reg_id_t find_register(char *name); +/* Return name of register given its ID */ +char *reg_name(reg_id_t id); + +/**************** Instruction Encoding **************/ + +/* Different argument types */ +typedef enum { R_ARG, M_ARG, I_ARG, NO_ARG } arg_t; + +/* Different instruction types */ +typedef enum { I_HALT, I_NOP, I_RRMOVQ, I_IRMOVQ, I_RMMOVQ, I_MRMOVQ, + I_ALU, I_JMP, I_CALL, I_RET, I_PUSHQ, I_POPQ, + I_IADDQ, I_POP2 } itype_t; + +/* Different ALU operations */ +typedef enum { A_ADD, A_SUB, A_AND, A_XOR, A_NONE } alu_t; + +/* Default function code */ +typedef enum { F_NONE } fun_t; + +/* Return name of operation given its ID */ +char op_name(alu_t op); + +/* Different Jump conditions */ +typedef enum { C_YES, C_LE, C_L, C_E, C_NE, C_GE, C_G } cond_t; + +/* Pack itype and function into single byte */ +#define HPACK(hi,lo) ((((hi)&0xF)<<4)|((lo)&0xF)) + +/* Unpack byte */ +#define HI4(byte) (((byte)>>4)&0xF) +#define LO4(byte) ((byte)&0xF) + +/* Get the opcode out of one byte instruction field */ +#define GET_ICODE(instr) HI4(instr) + +/* Get the ALU/JMP function out of one byte instruction field */ +#define GET_FUN(instr) LO4(instr) + +/* Return name of instruction given it's byte encoding */ +char *iname(int instr); + +/**************** Truth Values **************/ +typedef enum { FALSE, TRUE } bool_t; + +/* Table used to encode information about instructions */ +typedef struct { + char *name; + unsigned char code; /* Byte code for instruction+op */ + int bytes; + arg_t arg1; + int arg1pos; + int arg1hi; /* 0/1 for register argument, # bytes for allocation */ + arg_t arg2; + int arg2pos; + int arg2hi; /* 0/1 */ +} instr_t, *instr_ptr; + +instr_ptr find_instr(char *name); + +/* Return invalid instruction for error handling purposes */ +instr_ptr bad_instr(); + +/*********** Implementation of Memory *****************/ +typedef unsigned char byte_t; +typedef long long int word_t; +typedef long long unsigned uword_t; + +/* Represent a memory as an array of bytes */ +typedef struct { + int len; + word_t maxaddr; + byte_t *contents; +} mem_rec, *mem_t; + +/* Create a memory with len bytes */ +mem_t init_mem(int len); +void free_mem(mem_t m); + +/* Set contents of memory to 0 */ +void clear_mem(mem_t m); + +/* Make a copy of a memory */ +mem_t copy_mem(mem_t oldm); +/* Print the differences between two memories */ +bool_t diff_mem(mem_t oldm, mem_t newm, FILE *outfile); + +/* How big should the memory be? */ +#ifdef BIG_MEM +#define MEM_SIZE (1<<16) +#else +#define MEM_SIZE (1<<13) +#endif + +/*** In the following functions, a return value of 1 means success ***/ + +/* Load memory from .yo file. Return number of bytes read */ +int load_mem(mem_t m, FILE *infile, int report_error); + +/* Get byte from memory */ +bool_t get_byte_val(mem_t m, word_t pos, byte_t *dest); + +/* Get 8 bytes from memory */ +bool_t get_word_val(mem_t m, word_t pos, word_t *dest); + +/* Set byte in memory */ +bool_t set_byte_val(mem_t m, word_t pos, byte_t val); + +/* Set 8 bytes in memory */ +bool_t set_word_val(mem_t m, word_t pos, word_t val); + +/* Print contents of memory */ +void dump_memory(FILE *outfile, mem_t m, word_t pos, int cnt); + +/********** Implementation of Register File *************/ + +mem_t init_reg(); +void free_reg(mem_t t); + +/* Make a copy of a register file */ +mem_t copy_reg(mem_t oldr); +/* Print the differences between two register files */ +bool_t diff_reg(mem_t oldr, mem_t newr, FILE *outfile); + + +word_t get_reg_val(mem_t r, reg_id_t id); +void set_reg_val(mem_t r, reg_id_t id, word_t val); +void dump_reg(FILE *outfile, mem_t r); + + + +/* **************** ALU Function **********************/ + +/* Compute ALU operation */ +word_t compute_alu(alu_t op, word_t arg1, word_t arg2); + +typedef unsigned char cc_t; + +#define GET_ZF(cc) (((cc) >> 2)&0x1) +#define GET_SF(cc) (((cc) >> 1)&0x1) +#define GET_OF(cc) (((cc) >> 0)&0x1) + +#define PACK_CC(z,s,o) (((z)<<2)|((s)<<1)|((o)<<0)) + +#define DEFAULT_CC PACK_CC(1,0,0) + +/* Compute condition code. */ +cc_t compute_cc(alu_t op, word_t arg1, word_t arg2); + +/* Generated printed form of condition code */ +char *cc_name(cc_t c); + +/* **************** Status types *******************/ + +typedef enum + {STAT_BUB, STAT_AOK, STAT_HLT, STAT_ADR, STAT_INS, STAT_PIP } stat_t; + +/* Describe Status */ +char *stat_name(stat_t e); + +/* **************** ISA level implementation *********/ + +typedef struct { + word_t pc; + mem_t r; + mem_t m; + cc_t cc; +} state_rec, *state_ptr; + +state_ptr new_state(int memlen); +void free_state(state_ptr s); + +state_ptr copy_state(state_ptr s); +bool_t diff_state(state_ptr olds, state_ptr news, FILE *outfile); + +/* Determine if condition satisified */ +bool_t cond_holds(cc_t cc, cond_t bcond); + +/* Execute single instruction. Return status. */ +stat_t step_state(state_ptr s, FILE *error_file); + +/************************ Interface Functions *************/ + +#ifdef HAS_GUI +void report_line(word_t line_no, word_t addr, char *hexcode, char *line); +void signal_register_update(reg_id_t r, word_t val); + +#endif diff --git a/arch/sim/misc/mux4.hcl b/arch/sim/misc/mux4.hcl new file mode 100644 index 0000000..6e14466 --- /dev/null +++ b/arch/sim/misc/mux4.hcl @@ -0,0 +1,57 @@ +#/* $begin sim-mux4-raw-hcl */ +## Simple example of an HCL file. +## This file can be converted to C using hcl2c, and then compiled. + +## In this example, we will generate the MUX4 circuit shown in +## Section SLASHrefLBRACKsect:arch:hclsetRBRACK. It consists of a control block that generates +## bit-level signals s1 and s0 from the input signal code, +## and then uses these signals to control a 4-way multiplexor +## with data inputs A, B, C, and D. + +## This code is embedded in a C program that reads +## the values of code, A, B, C, and D from the command line +## and then prints the circuit output + +## Information that is inserted verbatim into the C file +quote '#include ' +quote '#include ' +quote 'long long code_val, s0_val, s1_val;' +quote 'char **data_names;' + +## Declarations of signals used in the HCL description and +## the corresponding C expressions. +boolsig s0 's0_val' +boolsig s1 's1_val' +wordsig code 'code_val' +wordsig A 'atoll(data_names[0])' +wordsig B 'atoll(data_names[1])' +wordsig C 'atoll(data_names[2])' +wordsig D 'atoll(data_names[3])' + +## HCL descriptions of the logic blocks +quote '/* $begin sim-mux4-s1-c */' +bool s1 = code in { 2, 3 }; +quote '/* $end sim-mux4-s1-c */' + +bool s0 = code in { 1, 3 }; + +word Out4 = [ + !s1 && !s0 : A; # 00 + !s1 : B; # 01 + !s0 : C; # 10 + 1 : D; # 11 +]; + +## More information inserted verbatim into the C code to +## compute the values and print the output +quote '/* $begin sim-mux4-main-c */' +quote 'int main(int argc, char *argv[]) {' +quote ' data_names = argv+2;' +quote ' code_val = atoll(argv[1]);' +quote ' s1_val = gen_s1();' +quote ' s0_val = gen_s0();' +quote ' printf("Out = %lld\n", gen_Out4());' +quote ' return 0;' +quote '}' +quote '/* $end sim-mux4-main-c */' +#/* $end sim-mux4-raw-hcl */ diff --git a/arch/sim/misc/node.c b/arch/sim/misc/node.c new file mode 100644 index 0000000..1f35938 --- /dev/null +++ b/arch/sim/misc/node.c @@ -0,0 +1,703 @@ +/* Functions to generate C or Verilog code from HCL */ +/* This file maintains a parse tree representation of expressions */ + +#include +#include +#include +#include +#include + +#include "node.h" +#include "outgen.h" + +#define MAXBUF 1024 + +void yyerror(const char *str); +void yyserror(const char *str, char *other); + +/* For error reporting */ +static char* show_expr(node_ptr expr); + +/* The symbol table */ +#define SYM_LIM 100 +static node_ptr sym_tab[2][SYM_LIM]; +static int sym_count = 0; + +/* Optional simulator name */ +char simname[MAXBUF] = ""; + +#ifdef UCLID +int annotate = 0; +/* Keep list of argument names encountered in node definition */ +char *arg_names[SYM_LIM]; +int arg_cnt = 0; +#endif + + +extern FILE *outfile; + +/* + * usage - print helpful diagnostic information + */ +static void usage(char *name) +{ +#ifdef VLOG + fprintf(stderr, "Usage: %s [-h] < HCL_file > verilog_file\n", name); +#else +#ifdef UCLID + fprintf(stderr, "Usage: %s [-ah] < HCL_file > uclid_file\n", name); + fprintf(stderr, " -a Add define/use annotations\n"); +#else /* !UCLID */ + fprintf(stderr, "Usage: %s [-h][-n NAM] < HCL_file > C_file\n", name); +#endif /* UCLID */ +#endif /* VLOG */ + fprintf(stderr, " -h Print this message\n"); + fprintf(stderr, " -n NAM Specify processor name\n"); + exit(0); +} + + +/* Initialization */ +void init_node(int argc, char **argv) +{ + int c; + int max_column = 75; + int first_indent = 4; + int other_indents = 2; + + /* Parse the command line arguments */ + while ((c = getopt(argc, argv, "hna")) != -1) { + switch(c) { + case 'h': + usage(argv[0]); + break; + case 'n': /* Optional simulator name */ + strcpy(simname, argv[optind]); + break; +#ifdef UCLID + case 'a': + annotate = 1; + break; +#endif + default: + printf("Invalid option '%c'\n", c); + usage(argv[0]); + break; + } + } + +#if !defined(VLOG) && !defined(UCLID) + /* Define and initialize the simulator name */ + if (!strcmp(simname, "")) + printf("char simname[] = \"Y86-64 Processor\";\n"); + else + printf("char simname[] = \"Y86-64 Processor: %s\";\n", simname); +#endif + outgen_init(outfile, max_column, first_indent, other_indents); +} + +static void add_symbol(node_ptr name, node_ptr val) +{ + if (sym_count >= SYM_LIM) { + yyerror("Symbol table limit exceeded"); + return; + } + sym_tab[0][sym_count] = name; + sym_tab[1][sym_count] = val; + sym_count++; +} + + +static char *node_names[] = + {"quote", "var", "num", "and", "or", "not", "comp", "ele", "case"}; + +static void show_node(node_ptr node) +{ + printf("Node type: %s, Boolean ? %c, String value: %s\n", + node_names[node->type], node->isbool ? 'Y':'N', node->sval); +} + + +void finish_node(int check_ref) +{ + if (check_ref) { + int i; + for (i = 0; i < sym_count; i++) + if (!sym_tab[0][i]->ref) { + fprintf(stderr, "Warning, argument '%s' not referenced\n", + sym_tab[0][i]->sval); + } + } +} + +static node_ptr find_symbol(char *name) +{ + int i; + for (i = 0; i < sym_count; i++) { + if (strcmp(name, sym_tab[0][i]->sval) == 0) { + node_ptr result = sym_tab[1][i]; + sym_tab[0][i]->ref++; + return result; + } + } + yyserror("Symbol %s not found", name); + return NULL; +} + +#ifdef UCLID +/* See if string should be considered argument. + Currently, omit strings that are all upper case */ +static int is_arg(char *name) +{ + int upper = 1; + int c; + while ((c=*name++) != '\0') + upper = upper && isupper(c); + return !upper; +} + +/* See if string is part of current argument list */ +static void check_for_arg(char *name) +{ + int i; + if (!is_arg(name)) + return; + for (i = 0; i < arg_cnt; i++) + if (strcmp(arg_names[i], name) == 0) + return; + arg_names[arg_cnt++] = name; +} +#endif + +static node_ptr new_node(node_type_t t, int isbool, + char *s, node_ptr a1, node_ptr a2) +{ + node_ptr result = malloc(sizeof(node_rec)); + result->type = t; + result->isbool = isbool; + result->sval = s; + result->arg1 = a1; + result->arg2 = a2; + result->ref = 0; + result->next = NULL; + return result; +} + +/* Concatenate two lists */ +node_ptr concat(node_ptr n1, node_ptr n2) +{ + node_ptr tail = n1; + if (!n1) + return n2; + while (tail->next) + tail = tail->next; + tail->next = n2; + return n1; +} + +static void free_node(node_ptr n) +{ + free(n->sval); + free(n); +} + +node_ptr make_quote(char *qstring) +{ + + /* Quoted string still has quotes around it */ + int len = strlen(qstring)-2; + char *sname = malloc(len+1); + strncpy(sname, qstring+1, len); + sname[len] = '\0'; + return new_node(N_QUOTE, 0, sname, NULL, NULL); +} + +node_ptr make_var(char *name) +{ + char *sname = malloc(strlen(name)+1); + strcpy(sname, name); + /* Initially assume var is not Boolean */ + return new_node(N_VAR, 0, sname, NULL, NULL); +} + +node_ptr make_num(char *name) +{ + char *sname = malloc(strlen(name)+1); + strcpy(sname, name); + return new_node(N_NUM, 0, sname, NULL, NULL); +} + +void set_bool(node_ptr varnode) +{ + if (!varnode) + yyerror("Null node encountered"); + varnode->isbool = 1; +} + +/* Make sure argument is OK */ +static int check_arg(node_ptr arg, int wantbool) +{ + if (!arg) { + yyerror("Null node encountered"); + return 0; + } + if (arg->type == N_VAR) { + node_ptr qval = find_symbol(arg->sval); + if (!qval) { + yyserror("Variable '%s' not found", arg->sval); + return 0; + } + if (wantbool != qval->isbool) { + if (wantbool) + yyserror("Variable '%s' not Boolean", arg->sval); + else + yyserror("Variable '%s' not integer", arg->sval); + return 0; + } + return 1; + } + if (arg->type == N_NUM) { + if (wantbool && strcmp(arg->sval,"0") != 0 && + strcmp(arg->sval,"1") != 0) { + yyserror("Value '%s' not Boolean", arg->sval); + return 0; + } + return 1; + } + if (wantbool && !arg->isbool) + yyserror("Non Boolean argument '%s'", show_expr(arg)); + if (!wantbool && arg->isbool) + yyserror("Non integer argument '%s'", show_expr(arg)); + return (wantbool == arg->isbool); +} + +node_ptr make_not(node_ptr arg) +{ + check_arg(arg, 1); + return new_node(N_NOT, 1, "!", arg, NULL); +} + +node_ptr make_and(node_ptr arg1, node_ptr arg2) +{ + check_arg(arg1, 1); + check_arg(arg2, 1); + return new_node(N_AND, 1, "&", arg1, arg2); +} + +node_ptr make_or(node_ptr arg1, node_ptr arg2) +{ + check_arg(arg1, 1); + check_arg(arg2, 1); + return new_node(N_OR, 1, "|", arg1, arg2); +} + +node_ptr make_comp(node_ptr op, node_ptr arg1, node_ptr arg2) +{ + check_arg(arg1, 0); + check_arg(arg2, 0); + return new_node(N_COMP, 1, op->sval, arg1, arg2); +} + +node_ptr make_ele(node_ptr arg1, node_ptr arg2) +{ + node_ptr ele; + check_arg(arg1, 0); + for (ele = arg1; ele; ele = ele->next) + check_arg(ele, 0); + return new_node(N_ELE, 1, "in", arg1, arg2); +} + +node_ptr make_case(node_ptr arg1, node_ptr arg2) +{ + check_arg(arg1, 1); + check_arg(arg2, 0); + return new_node(N_CASE, 0, ":", arg1, arg2); +} + +void insert_code(node_ptr qstring) +{ + if (!qstring) + yyerror("Null node"); + else { +#if !defined(VLOG) && !defined(UCLID) + fputs(qstring->sval, outfile); + fputs("\n", outfile); +#endif + } +} + +void add_arg(node_ptr var, node_ptr qstring, int isbool) +{ + if (!var || !qstring) { + yyerror("Null node"); + return; + } + add_symbol(var, qstring); + if (isbool) { + set_bool(var); + set_bool(qstring); + } +} + +static char expr_buf[1024]; +static int errlen = 0; +#define MAXERRLEN 80 + +/* Recursively display expression for error reporting */ +static void show_expr_helper(node_ptr expr) +{ + switch(expr->type) { + int len; + node_ptr ele; + case N_QUOTE: + len = strlen(expr->sval) + 2; + if (len + errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "'%s'", expr->sval); + errlen += len; + } + break; + case N_VAR: + len = strlen(expr->sval); + if (len + errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "%s", expr->sval); + errlen += len; + } + break; + case N_NUM: + len = strlen(expr->sval); + if (len + errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "%s", expr->sval); + errlen += len; + } + break; + case N_AND: + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "("); + errlen+=1; + show_expr_helper(expr->arg1); + sprintf(expr_buf+errlen, " & "); + errlen+=3; + } + if (errlen < MAXERRLEN) { + show_expr_helper(expr->arg2); + sprintf(expr_buf+errlen, ")"); + errlen+=1; + } + break; + case N_OR: + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "("); + errlen+=1; + show_expr_helper(expr->arg1); + sprintf(expr_buf+errlen, " | "); + errlen+=3; + } + if (errlen < MAXERRLEN) { + show_expr_helper(expr->arg2); + sprintf(expr_buf+errlen, ")"); + errlen+=1; + } + break; + case N_NOT: + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "!"); + errlen+=1; + show_expr_helper(expr->arg1); + } + break; + case N_COMP: + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "("); + errlen+=1; + show_expr_helper(expr->arg1); + sprintf(expr_buf+errlen, " %s ", expr->sval); + errlen+=4; + } + if (errlen < MAXERRLEN) { + show_expr_helper(expr->arg2); + sprintf(expr_buf+errlen, ")"); + errlen+=1; + } + break; + case N_ELE: + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "("); + errlen+=1; + show_expr_helper(expr->arg1); + sprintf(expr_buf+errlen, " in {"); + errlen+=5; + } + for (ele = expr->arg2; ele; ele=ele->next) { + if (errlen < MAXERRLEN) { + show_expr_helper(ele); + if (ele->next) { + sprintf(expr_buf+errlen, ", "); + errlen+=2; + } + } + } + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "})"); + errlen+=2; + } + break; + case N_CASE: + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "[ "); + errlen+=2; + } + for (ele = expr; errlen < MAXERRLEN && ele; ele=ele->next) { + show_expr_helper(ele->arg1); + sprintf(expr_buf+errlen, " : "); + errlen += 3; + show_expr_helper(ele->arg2); + } + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, " ]"); + errlen+=2; + } + break; + default: + if (errlen < MAXERRLEN) { + sprintf(expr_buf+errlen, "??"); + errlen+=2; + } + break; + } +} + +static char *show_expr(node_ptr expr) +{ + errlen = 0; + show_expr_helper(expr); + if (errlen >= MAXERRLEN) + sprintf(expr_buf+errlen, "..."); + return expr_buf; +} + +/* Recursively generate code for function */ +static void gen_expr(node_ptr expr) +{ + node_ptr ele; + switch(expr->type) { + case N_QUOTE: + yyserror("Unexpected quoted string", expr->sval); + break; + case N_VAR: + { + node_ptr qstring = find_symbol(expr->sval); + if (qstring) +#if defined(VLOG) || defined(UCLID) + outgen_print("%s", expr->sval); +#else + outgen_print("(%s)", qstring->sval); +#endif + else + yyserror("Invalid variable '%s'", expr->sval); +#ifdef UCLID + check_for_arg(expr->sval); +#endif + + } + break; + case N_NUM: +#ifdef UCLID + { + long long int val = atoll(expr->sval); + if (val < -1) + outgen_print("pred^%d(CZERO)", -val); + else if (val == -1) + outgen_print("pred(CZERO)"); + else if (val == 0) + outgen_print("CZERO"); + else if (val == 1) + outgen_print("succ(CZERO)"); + else + outgen_print("succ^%d(CZERO)", val); + } +#else /* !UCLID */ + fputs(expr->sval, outfile); +#endif /* UCLID */ + break; + case N_AND: + outgen_print("("); + outgen_upindent(); + gen_expr(expr->arg1); + outgen_print(" & "); + gen_expr(expr->arg2); + outgen_print(")"); + outgen_downindent(); + break; + case N_OR: + outgen_print("("); + outgen_upindent(); + gen_expr(expr->arg1); + outgen_print(" | "); + gen_expr(expr->arg2); + outgen_print(")"); + outgen_downindent(); + break; + case N_NOT: +#if defined(VLOG) || defined(UCLID) + outgen_print("~"); +#else + outgen_print("!"); +#endif + gen_expr(expr->arg1); + break; + case N_COMP: + outgen_print("("); + outgen_upindent(); + gen_expr(expr->arg1); +#ifdef UCLID + { + char *cval = expr->sval; + if (strcmp(cval, "==") == 0) + cval = "="; + outgen_print(" %s ", cval); + } +#else /* !UCLID */ + outgen_print(" %s ", expr->sval); +#endif /* UCLID */ + gen_expr(expr->arg2); + outgen_print(")"); + outgen_downindent(); + break; + case N_ELE: + outgen_print("("); + outgen_upindent(); + for (ele = expr->arg2; ele; ele=ele->next) { + gen_expr(expr->arg1); +#ifdef UCLID + outgen_print(" = "); +#else + outgen_print(" == "); +#endif + gen_expr(ele); + if (ele->next) +#if defined(VLOG) || defined(UCLID) + outgen_print(" | "); +#else + outgen_print(" || "); +#endif + } + outgen_print(")"); + outgen_downindent(); + break; + case N_CASE: +#ifdef UCLID + outgen_print("case"); + outgen_terminate(); + { + /* Use this to keep track of last case when no default is given */ + node_ptr last_arg2 = NULL; + for (ele = expr; ele; ele=ele->next) { + outgen_print(" "); + if (ele->arg1->type == N_NUM && atoll(ele->arg1->sval) == 1) { + outgen_print("default"); + last_arg2 = NULL; + } + else { + gen_expr(ele->arg1); + last_arg2 = ele->arg2; + } + outgen_print(" : "); + gen_expr(ele->arg2); + outgen_print(";"); + outgen_terminate(); + } + if (last_arg2) { + /* Use final case as default */ + outgen_print(" default : "); + gen_expr(last_arg2); + outgen_print(";"); + outgen_terminate(); + } + } + outgen_print(" esac"); +#else /* !UCLID */ + outgen_print("("); + outgen_upindent(); + int done = 0; + for (ele = expr; ele && !done; ele=ele->next) { + if (ele->arg1->type == N_NUM && atoll(ele->arg1->sval) == 1) { + gen_expr(ele->arg2); + done = 1; + } else { + gen_expr(ele->arg1); + outgen_print(" ? "); + gen_expr(ele->arg2); + outgen_print(" : "); + } + } + if (!done) + outgen_print("0"); + outgen_print(")"); + outgen_downindent(); +#endif + break; + default: + yyerror("Unknown node type"); + break; + } +} + + +/* Generate code defining function for var */ +void gen_funct(node_ptr var, node_ptr expr, int isbool) +{ + if (!var || !expr) { + yyerror("Null node"); + return; + } + check_arg(expr, isbool); +#ifdef VLOG + outgen_print("assign %s = ", var->sval); + outgen_terminate(); + outgen_print(" "); + gen_expr(expr); + outgen_print(";"); + outgen_terminate(); + outgen_terminate(); +#else /* !VLOG */ +#ifdef UCLID + if (annotate) { + /* Print annotation information*/ + outgen_print("(* $define %s *)", var->sval); + outgen_terminate(); + } + outgen_print("%s := ", var->sval); + outgen_terminate(); + outgen_print(" "); + if (isbool && expr->type == N_NUM) { + outgen_print("%d", atoll(var->sval)); + } else + gen_expr(expr); + outgen_print(";"); + outgen_terminate(); + if (annotate) { + int i; + outgen_print("(* $args"); + for (i = 0; i < arg_cnt; i++) + outgen_print("%c%s", i == 0 ? ' ' : ':', arg_names[i]); + outgen_print(" *)"); + outgen_terminate(); + arg_cnt = 0; + } + outgen_terminate(); +#else /* !UCLID */ + /* Print function header */ + outgen_print("long long gen_%s()", var->sval); + outgen_terminate(); + outgen_print("{"); + outgen_terminate(); + outgen_print(" return "); + gen_expr(expr); + outgen_print(";"); + outgen_terminate(); + outgen_print("}"); + outgen_terminate(); + outgen_terminate(); +#endif /* UCLID */ +#endif /* VLOG */ +} diff --git a/arch/sim/misc/node.h b/arch/sim/misc/node.h new file mode 100644 index 0000000..b362592 --- /dev/null +++ b/arch/sim/misc/node.h @@ -0,0 +1,35 @@ +#ifndef NODE_H +typedef enum { N_QUOTE, N_VAR, N_NUM, N_AND, N_OR, N_NOT, N_COMP, N_ELE, N_CASE } node_type_t; + +typedef struct NODE { + node_type_t type; + int isbool; /* Is this node a Boolean expression? */ + char *sval; + struct NODE *arg1; + struct NODE *arg2; + int ref; /* For var, how many times has it been referenced? */ + struct NODE *next; +} node_rec, *node_ptr; + +void init_node(int argc, char **argv); +void finish_node(int check_ref); + +node_ptr make_quote(char *qstring); +node_ptr make_var(char *name); +node_ptr make_num(char *name); +void set_bool(node_ptr varnode); +node_ptr make_not(node_ptr arg); +node_ptr make_and(node_ptr arg1, node_ptr arg2); +node_ptr make_or(node_ptr arg1, node_ptr arg2); +node_ptr make_comp(node_ptr op, node_ptr arg1, node_ptr arg2); +node_ptr make_ele(node_ptr arg1, node_ptr arg2); +node_ptr make_case(node_ptr arg1, node_ptr arg2); + +node_ptr concat(node_ptr n1, node_ptr n2); + +void insert_code(node_ptr qstring); +void add_arg(node_ptr var, node_ptr qstring, int isbool); +void gen_funct(node_ptr var, node_ptr expr, int isbool); +#define NODE_H +#endif + diff --git a/arch/sim/misc/outgen.c b/arch/sim/misc/outgen.c new file mode 100644 index 0000000..e51c65f --- /dev/null +++ b/arch/sim/misc/outgen.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +#include "outgen.h" +/* Output generator that ensures no line exceeds specified number of columns */ + +#define STRING_LENGTH 1024 + +FILE *outfile = NULL; +int max_column = 80; +int first_indent = 4; +int other_indents = 2; +int cur_pos = 0; +int indent = 0; + + +/* Controlling parameters */ +void outgen_init(FILE *arg_outfile, int arg_max_column, int arg_first_indent, int arg_other_indents) { + outfile = arg_outfile; + max_column = arg_max_column; + first_indent = arg_first_indent; + other_indents = arg_other_indents; + cur_pos = 0; + indent = first_indent; +} + +static void print_token(char *string) { + if (outfile == NULL) + outfile = stdout; + int len = strlen(string); + int i; + if (len+cur_pos > max_column) { + fprintf(outfile, "\n"); + for (i = 0; i < indent; i++) + fprintf(outfile, " "); + cur_pos = indent; + } + fprintf(outfile, "%s", string); + cur_pos += len; +} + + +/* Terminate statement and reset indentations */ +void outgen_terminate() { + printf("\n"); + cur_pos = 0; + indent = first_indent; +} + +/* Output generator printing */ +void outgen_print(char *fmt, ...) { + char buf[STRING_LENGTH]; + va_list argp; + va_start(argp, fmt); + vsprintf(buf, fmt, argp); + va_end(argp); + print_token(buf); +} + +/* Increase indentation level */ +void outgen_upindent() { + indent += other_indents; +} +/* Decrease indentation level */ +void outgen_downindent() { + indent -= other_indents; +} + + diff --git a/arch/sim/misc/outgen.h b/arch/sim/misc/outgen.h new file mode 100644 index 0000000..1a48702 --- /dev/null +++ b/arch/sim/misc/outgen.h @@ -0,0 +1,18 @@ +/* Output generator that ensures no line exceeds specified number of columns */ + +/* Controlling parameters */ +void outgen_init(FILE *outfile, int max_column, int first_indent, int other_indents); + +/* Terminate statement and reset indentations */ +void outgen_terminate(); + +/* Output generator printing */ +void outgen_print(char *fmt, ...); + +/* Increase indentation level */ +void outgen_upindent(); +/* Decrease indentation level */ +void outgen_downindent(); + + + diff --git a/arch/sim/misc/rsum.ys b/arch/sim/misc/rsum.ys new file mode 100644 index 0000000..2d9c48b --- /dev/null +++ b/arch/sim/misc/rsum.ys @@ -0,0 +1,37 @@ + .pos 0 + irmovq stack, %rsp + call main + halt + +# Sample linked list + .align 8 +ele1: + .quad 0x00a + .quad ele2 +ele2: + .quad 0x0b0 + .quad ele3 +ele3: + .quad 0xc00 + .quad 0 + +main: + irmovq ele1, %rdi + call rsum_list + ret + +rsum_list: + xorq %rax, %rax + andq %rdi, %rdi + je end + pushq %rdi + mrmovq 8(%rdi), %rdi + call rsum_list + popq %rdi + mrmovq (%rdi), %rsi + addq %rsi, %rax +end: + ret + + .pos 0x200 +stack: diff --git a/arch/sim/misc/sum.ys b/arch/sim/misc/sum.ys new file mode 100644 index 0000000..f9aa8df --- /dev/null +++ b/arch/sim/misc/sum.ys @@ -0,0 +1,36 @@ + .pos 0 + irmovq stack, %rsp + call main + halt + +# Sample linked list + .align 8 +ele1: + .quad 0x00a + .quad ele2 +ele2: + .quad 0x0b0 + .quad ele3 +ele3: + .quad 0xc00 + .quad 0 + +main: + irmovq ele1, %rdi + call sum_list + ret + +sum_list: + xorq %rax, %rax + jmp test +loop: + mrmovq (%rdi), %rsi + addq %rsi, %rax + mrmovq 8(%rdi), %rdi +test: + andq %rdi, %rdi + jne loop + ret + + .pos 0x200 +stack: diff --git a/arch/sim/misc/yas-grammar.lex b/arch/sim/misc/yas-grammar.lex new file mode 100644 index 0000000..be03756 --- /dev/null +++ b/arch/sim/misc/yas-grammar.lex @@ -0,0 +1,39 @@ +/* Grammar for Y86-64 Assembler */ + #include "yas.h" + +Instr rrmovq|cmovle|cmovl|cmove|cmovne|cmovge|cmovg|rmmovq|mrmovq|irmovq|addq|subq|andq|xorq|jmp|jle|jl|je|jne|jge|jg|call|ret|pushq|popq|"."byte|"."word|"."long|"."quad|"."pos|"."align|halt|nop|iaddq +Letter [a-zA-Z] +Digit [0-9] +Ident {Letter}({Letter}|{Digit}|_)* +Hex [0-9a-fA-F] +Blank [ \t] +Newline [\n\r] +Return [\r] +Char [^\n\r] +Reg %rax|%rcx|%rdx|%rbx|%rsi|%rdi|%rsp|%rbp|%r8|%r9|%r10|%r11|%r12|%r13|%r14 + +%x ERR COM +%% + +^{Char}*{Return}*{Newline} { save_line(yytext); REJECT;} /* Snarf input line */ +#{Char}*{Return}*{Newline} {finish_line(); lineno++;} +"//"{Char}*{Return}*{Newline} {finish_line(); lineno++;} +"/*"{Char}*{Return}*{Newline} {finish_line(); lineno++;} +{Blank}*{Return}*{Newline} {finish_line(); lineno++;} + +{Blank}+ ; +"$"+ ; +{Instr} add_instr(yytext); +{Reg} add_reg(yytext); +[-]?{Digit}+ add_num(atoll(yytext)); +"0"[xX]{Hex}+ add_num(atollh(yytext)); +[():,] add_punct(*yytext); +{Ident} add_ident(yytext); +{Char} {; BEGIN ERR;} +{Char}*{Newline} {fail("Invalid line"); lineno++; BEGIN 0;} +%% + +unsigned int atoh(const char *s) +{ + return(strtoul(s, NULL, 16)); +} diff --git a/arch/sim/misc/yas.c b/arch/sim/misc/yas.c new file mode 100644 index 0000000..f36b1b6 --- /dev/null +++ b/arch/sim/misc/yas.c @@ -0,0 +1,628 @@ +/* Assembler for Y86-64 instruction set */ + +#include +#include +#include + +#include "yas.h" +#include "isa.h" + +void add_symbol(char *, int); +int find_symbol(char *); +int instr_size(char *); + +int gui_mode = 0; + +FILE *outfile; + +int verbose = 0; +/* Generate initialized memory for Verilog? */ +int vcode = 0; + +/* Should it generate code for banked memory? */ +int block_factor = 0; + +int lineno = 1; /* Line number of input file */ +int bytepos = 0; /* Address of current instruction being processed */ +int error_mode = 0; /* Am I trying to finish off a line with an error? */ +int hit_error = 0; /* Have I hit any errors? */ + +int pass = 1; /* Am I in pass 1 or 2? */ + +/* General strategy is to read tokens for a complete line and then + process them. +*/ +#define TOK_PER_LINE 12 + +/* Token types */ +typedef enum{ TOK_IDENT, TOK_NUM, TOK_REG, TOK_INSTR, TOK_PUNCT, TOK_ERR } + token_t; + +/* Token representation */ +typedef struct { + char *sval; /* String */ + word_t ival; /* Integer */ + char cval; /* Character */ + token_t type; /* Type */ +} token_rec, *token_ptr; + +/* Information about current input line */ +token_rec tokens[TOK_PER_LINE]; +int lineno; /* What line number am I processing? */ +int bytepos; /* What byte address is the current instruction */ +int tcount; /* How many tokens are there in this line? */ +int tpos; /* What token am I currently processing */ + +/* Storage for strings in current line */ +#define STRMAX 4096 +char strbuf[STRMAX]; +int strpos; + +/* Storage of current line */ +char input_line[STRMAX]; + +void save_line(char *s) +{ + int len = strlen(s); + int i; + if (len >= STRMAX) + fail("Input Line too long"); + strcpy(input_line, s); + for (i = len-1; input_line[i] == '\n' || input_line[i] == '\r'; i--) + input_line[i] = '\0'; /* Remove terminator */ +} + +/* Information about current instruction being generated */ +char code[10]; /* Byte encoding */ +int codepos = 0; /* Current position in byte encoding */ +int bcount = 0; /* Length of current instruction */ + +/* Debugging information */ +char token_type_names[] = {'I', 'N', 'R', 'X', 'P'}; + +void print_token(FILE *out, token_ptr t) +{ + fprintf(out, " [%c ", token_type_names[t->type]); + switch(t->type) { + case TOK_IDENT: + case TOK_REG: + case TOK_INSTR: + fprintf(out, "%s]", t->sval); + break; + case TOK_NUM: + fprintf(out, "%lld]", t->ival); + break; + case TOK_PUNCT: + fprintf(out, "%c]", t->cval); + break; + case TOK_ERR: + fprintf(out, "ERR]"); + break; + default: + fprintf(out, "?]"); + fail("Unknown token type"); + } +} + +/* For debugging */ +void print_instruction(FILE *out) +{ + int i; + fprintf(out, "Line %d, Byte %d: ", lineno, bytepos); + for (i = 0; i < tcount; i++) + print_token(out, &tokens[i]); + fprintf(out, " Code: "); + for (i = 0; i < bcount; i++) + fprintf(out, "%.2x ", code[i] & 0xFF); + fprintf(out, "\n"); +} + +/* Write len least significant hex digits of value at dest. + Don't null terminate */ +static void hexstuff(char *dest, word_t value, int len) +{ + int i; + for (i = 0; i < len; i++) { + char c; + int h = (value >> 4*i) & 0xF; + c = h < 10 ? h + '0' : h - 10 + 'a'; + dest[len-i-1] = c; + } +} + +void print_code(FILE *out, int pos) +{ + char outstring[33]; + if (pos > 0xFFF) { + /* Printing format: + 0xHHHH: cccccccccccccccccccc | + where HHHH is address + cccccccccccccccccccc is code + */ + if (tcount) { + int i; + if (pos > 0xFFFF) { + fail("Code address limit exceeded"); + exit(1); + } + strcpy(outstring, "0x0000: | "); + hexstuff(outstring+2, pos, 4); + for (i = 0; i < bcount; i++) + hexstuff(outstring+7+2*i, code[i]&0xFF, 2); + } + else + strcpy(outstring, " | "); + } else { + /* Printing format: + 0xHHH: cccccccccccccccccccc | + where HHH is address + cccccccccccccccccccc is code + */ + if (tcount) { + int i; + if (pos > 0xFFF) { + fail("Code address limit exceeded"); + exit(1); + } + strcpy(outstring, "0x000: | "); + hexstuff(outstring+2, pos, 3); + for (i = 0; i < bcount; i++) + hexstuff(outstring+7+2*i, code[i]&0xFF, 2); + } + else + strcpy(outstring, " | "); + } + if (vcode) { + fprintf(out, "//%s%s\n", outstring, input_line); + if (tcount) { + int i; + for (i = 0; tcount && i < bcount; i++) { + if (block_factor) { + fprintf(out, " bank%d[%d] = 8\'h%.2x;\n", (pos+i)%block_factor, (pos+i)/block_factor, code[i] & 0xFF); + } else { + fprintf(out, " mem[%d] = 8\'h%.2x;\n", pos+i, code[i] & 0xFF); + } + } + } + } else { + fprintf(out, "%s%s\n", outstring, input_line); + } +} + +void fail(char *message) +{ + if (!error_mode) { + fprintf(stderr, "Error on line %d: %s\n", lineno, message); + fprintf(stderr, "Line %d, Byte 0x%.4x: %s\n", + lineno, bytepos, input_line); + } + error_mode = 1; + hit_error = 1; +} + +/* Parse Register from set of tokens and put into high or low + 4 bits of code[codepos] */ +void get_reg(int codepos, int hi) +{ + int rval = REG_NONE; + char c; + if (tokens[tpos].type != TOK_REG) { + fail("Expecting Register ID"); + return; + } else { + rval = find_register(tokens[tpos].sval); + } + /* Insert into output */ + c = code[codepos]; + if (hi) + c = (c & 0x0F) | (rval << 4); + else + c = (c & 0xF0) | rval; + code[codepos] = c; + tpos++; +} + +/* Get numeric value of given number of bytes */ +/* Offset indicates value to subtract from number (for PC relative) */ +void get_num(int codepos, int bytes, int offset) +{ + word_t val = 0; + int i; + if (tokens[tpos].type == TOK_NUM) { + val = tokens[tpos].ival; + } else if (tokens[tpos].type == TOK_IDENT) { + val = find_symbol(tokens[tpos].sval); + } else { + fail("Number Expected"); + return; + } + val -= offset; + for (i = 0; i < bytes; i++) + code[codepos+i] = (val >> (i * 8)) & 0xFF; + tpos++; +} + + +/* Get memory reference. + Can be of form: + Num(Reg) + (Reg) + Num + Ident + Ident(Reg) + Put Reg in low position of current byte, and Number in following bytes + */ +void get_mem(int codepos) +{ + char rval = REG_NONE; + word_t val = 0; + int i; + char c; + token_t type = tokens[tpos].type; + /* Deal with optional displacement */ + if (type == TOK_NUM) { + val = tokens[tpos++].ival; + type = tokens[tpos].type; + } else if (type == TOK_IDENT) { + val = find_symbol(tokens[tpos++].sval); + type = tokens[tpos].type; + } + /* Check for optional register */ + if (type == TOK_PUNCT) { + if (tokens[tpos].cval == '(') { + tpos++; + if (tokens[tpos].type == TOK_REG) + rval = find_register(tokens[tpos++].sval); + else { + fail("Expecting Register Id"); + return; + } + if (tokens[tpos].type != TOK_PUNCT || + tokens[tpos++].cval != ')') { + fail("Expecting ')'"); + return; + } + } + } + c = (code[codepos] & 0xF0) | (rval & 0xF); + code[codepos++] = c; + for (i = 0; i < 8; i++) + code[codepos+i] = (val >> (i*8)) & 0xFF; +} + +void start_line() +{ + int t; + error_mode = 0; + tpos = 0; + tcount = 0; + bcount = 0; + strpos = 0; + for (t = 0; t < TOK_PER_LINE; t++) + tokens[t].type = TOK_ERR; +} + +void finish_line() +{ + int size; + instr_ptr instr; + int savebytepos = bytepos; + tpos = 0; + codepos = 0; + if (tcount == 0) { + if (pass > 1) + print_code(outfile, savebytepos); + start_line(); + return; /* Empty line */ + } + /* Completion of an erroneous line */ + if (error_mode) { + start_line(); + return; + } + + /* See if this is a labeled line */ + if (tokens[0].type == TOK_IDENT) { + if (tokens[1].type != TOK_PUNCT || + tokens[1].cval != ':') { + fail("Missing Colon"); + start_line(); + return; + } else { + if (pass == 1) + add_symbol(tokens[0].sval, bytepos); + tpos+=2; + if (tcount == 2) { + /* That's all for this line */ + if (pass > 1) + print_code(outfile, savebytepos); + start_line(); + return; + } + } + } + /* Get instruction */ + if (tokens[tpos].type != TOK_INSTR) { + fail("Bad Instruction"); + start_line(); + return; + } + /* Process .pos */ + if (strcmp(tokens[tpos].sval, ".pos") == 0) { + if (tokens[++tpos].type != TOK_NUM) { + fail("Invalid Address"); + start_line(); + return; + } + bytepos = tokens[tpos].ival; + if (pass > 1) { + print_code(outfile, bytepos); + } + start_line(); + return; + } + /* Process .align */ + if (strcmp(tokens[tpos].sval, ".align") == 0) { + int a; + if (tokens[++tpos].type != TOK_NUM || (a=tokens[tpos].ival) <= 0) { + fail("Invalid Alignment"); + start_line(); + return; + } + bytepos = ((bytepos+a-1)/a)*a; + + if (pass > 1) { + print_code(outfile, bytepos); + } + start_line(); + return; + } + /* Get instruction size */ + instr = find_instr(tokens[tpos++].sval); + if (instr == NULL) { + fail("Invalid Instruction"); + instr = bad_instr(); + } + size = instr->bytes; + bytepos += size; + bcount = size; + + + /* If this is pass 1, then we're done */ + if (pass == 1) { + start_line(); + return; + } + + /* Here's where we really process the instructions */ + code[0] = instr->code; + code[1] = HPACK(REG_NONE, REG_NONE); + switch(instr->arg1) { + case R_ARG: + get_reg(instr->arg1pos, instr->arg1hi); + break; + case M_ARG: + get_mem(instr->arg1pos); + break; + case I_ARG: + get_num(instr->arg1pos, instr->arg1hi, 0); + break; + case NO_ARG: + default: + break; + } + if (instr->arg2 != NO_ARG) { + /* Get comma */ + if (tokens[tpos].type != TOK_PUNCT || + tokens[tpos].cval != ',') { + fail("Expecting Comma"); + start_line(); + return; + } + tpos++; + + /* Get second argument */ + switch(instr->arg2) { + case R_ARG: + get_reg(instr->arg2pos, instr->arg2hi); + break; + case M_ARG: + get_mem(instr->arg2pos); + break; + case I_ARG: + get_num(instr->arg2pos, instr->arg2hi, 0); + break; + case NO_ARG: + default: + break; + } + } + + print_code(outfile, savebytepos); + start_line(); +} + +void add_token(token_t type, char *s, word_t i, char c) +{ + char *t = NULL; + if (!tcount) + start_line(); + if (tpos >= TOK_PER_LINE-1) { + fail("Line too long"); + return; + } + if (s) { + int len = strlen(s)+1; + if (strpos + len > STRMAX) { + fail("Line too long"); + return; + } + t = strcpy(strbuf+strpos, s); + strpos+= len; + } + tokens[tcount].type = type; + tokens[tcount].sval = t; + tokens[tcount].ival = i; + tokens[tcount].cval = c; + tcount++; +} + +void add_ident(char *s) +{ + add_token(TOK_IDENT, s, 0, ' '); +} + +void add_instr(char *s) +{ + add_token(TOK_INSTR, s, 0, ' '); +} + +void add_reg(char *s) +{ + add_token(TOK_REG, s, 0, ' '); +} + +void add_num(long long i) +{ + add_token(TOK_NUM, NULL, i, ' '); +} + +void add_punct(char c) +{ + add_token(TOK_PUNCT, NULL, 0, c); +} + +#define STAB 1000 + +#define INIT_CNT 0 + +int symbol_cnt = INIT_CNT; +struct { + char *name; + int pos; +} symbol_table[STAB]; + +void add_symbol(char *name, int p) +{ + char *t = (char *) malloc(strlen(name)+1); + strcpy(t, name); + symbol_table[symbol_cnt].name = t; + symbol_table[symbol_cnt].pos = p; + symbol_cnt++; +} + +int find_symbol(char *name) +{ + int i; + for (i = 0; i < symbol_cnt; i++) + if (strcmp(name, symbol_table[i].name) == 0) + return symbol_table[i].pos; + fail("Can't find label"); + return -1; +} + +int yywrap() +{ + int i; + if (tcount > 0) { + fail("Missing end-of-line on final line\n"); + } + if (verbose && pass > 1) { + printf("Symbol Table:\n"); + for (i = INIT_CNT; i < symbol_cnt; i++) + printf(" %s\t0x%x\n", symbol_table[i].name, symbol_table[i].pos); + } + return 1; +} + +extern FILE *yyin; +int yylex(); + +static void usage(char *pname) +{ + printf("Usage: %s [-V[n]] file.ys\n", pname); + printf(" -V[n] Generate memory initialization in Verilog format (n-way blocking)\n"); + exit(0); +} + +int main(int argc, char *argv[]) +{ + int rootlen; + char infname[512]; + char outfname[512]; + int nextarg = 1; + if (argc < 2) + usage(argv[0]); + if (argv[nextarg][0] == '-') { + char flag = argv[nextarg][1]; + switch (flag) { + case 'V': + vcode = 1; + if (argv[nextarg][2]) { + block_factor = atoi(argv[nextarg]+2); + if (block_factor != 8) { + fprintf(stderr, "Unknown blocking factor %d\n", block_factor); + exit(1); + } + } + nextarg++; + break; + default: + usage(argv[0]); + } + } + rootlen = strlen(argv[nextarg])-3; + if (strcmp(argv[nextarg]+rootlen, ".ys")) + usage(argv[0]); + if (rootlen > 500) { + fprintf(stderr, "File name too long\n"); + exit(1); + } + strncpy(infname, argv[nextarg], rootlen); + strcpy(infname+rootlen, ".ys"); + + yyin = fopen(infname, "r"); + if (!yyin) { + fprintf(stderr, "Can't open input file '%s'\n", infname); + exit(1); + } + + if (vcode) { + outfile = stdout; + } else { + strncpy(outfname, argv[nextarg], rootlen); + strcpy(outfname+rootlen, ".yo"); + outfile = fopen(outfname, "w"); + if (!outfile) { + fprintf(stderr, "Can't open output file '%s'\n", outfname); + exit(1); + } + } + + pass = 1; + + yylex(); + fclose(yyin); + + if (hit_error) + exit(1); + + pass = 2; + lineno = 1; + error_mode = 0; + bytepos = 0; + yyin = fopen(infname, "r"); + if (!yyin) { + fprintf(stderr, "Can't open input file '%s'\n", infname); + exit(1); + } + + yylex(); + fclose(yyin); + fclose(outfile); + return hit_error; +} + +unsigned long long atollh(const char *p) { + return strtoull(p, (char **) NULL, 16); +} diff --git a/arch/sim/misc/yas.h b/arch/sim/misc/yas.h new file mode 100644 index 0000000..edbf6ff --- /dev/null +++ b/arch/sim/misc/yas.h @@ -0,0 +1,13 @@ +void save_line(char *); +void finish_line(); +void add_reg(char *); +void add_ident(char *); +void add_instr(char *); +void add_punct(char); +void add_num(long long); +void fail(char *msg); +unsigned long long atollh(const char *); + + +/* Current line number */ +int lineno; diff --git a/arch/sim/misc/yis.c b/arch/sim/misc/yis.c new file mode 100644 index 0000000..88e49d1 --- /dev/null +++ b/arch/sim/misc/yis.c @@ -0,0 +1,64 @@ +/* Instruction set simulator for Y86-64 Architecture */ + +#include +#include + +#include "isa.h" + +/* YIS never runs in GUI mode */ +int gui_mode = 0; + +void usage(char *pname) +{ + printf("Usage: %s code_file [max_steps]\n", pname); + exit(0); +} + +int main(int argc, char *argv[]) +{ + FILE *code_file; + int max_steps = 10000; + + state_ptr s = new_state(MEM_SIZE); + mem_t saver = copy_reg(s->r); + mem_t savem; + int step = 0; + + stat_t e = STAT_AOK; + + if (argc < 2 || argc > 3) + usage(argv[0]); + code_file = fopen(argv[1], "r"); + if (!code_file) { + fprintf(stderr, "Can't open code file '%s'\n", argv[1]); + exit(1); + } + + if (!load_mem(s->m, code_file, 1)) { + printf("Exiting\n"); + return 1; + } + + savem = copy_mem(s->m); + + if (argc > 2) + max_steps = atoi(argv[2]); + + for (step = 0; step < max_steps && e == STAT_AOK; step++) + e = step_state(s, stdout); + + printf("Stopped in %d steps at PC = 0x%llx. Status '%s', CC %s\n", + step, s->pc, stat_name(e), cc_name(s->cc)); + + printf("Changes to registers:\n"); + diff_reg(saver, s->r, stdout); + + printf("\nChanges to memory:\n"); + diff_mem(savem, s->m, stdout); + + free_state(s); + free_reg(saver); + free_mem(savem); + + return 0; +} diff --git a/arch/sim/pipe/Makefile b/arch/sim/pipe/Makefile new file mode 100644 index 0000000..0d46c3b --- /dev/null +++ b/arch/sim/pipe/Makefile @@ -0,0 +1,63 @@ +# Modify this line to indicate the default version to build + +VERSION=std + +# Comment this out if you don't have Tcl/Tk on your system + +GUIMODE=-DHAS_GUI + +# Modify the following line so that gcc can find the libtcl.so and +# libtk.so libraries on your system. You may need to use the -L option +# to tell gcc which directory to look in. Comment this out if you +# don't have Tcl/Tk. + +TKLIBS=-L/usr/lib -ltk -ltcl + +# Modify the following line so that gcc can find the tcl.h and tk.h +# header files on your system. Comment this out if you don't have +# Tcl/Tk. + +TKINC=-isystem /usr/include/tcl8.5 + +# Modify these two lines to choose your compiler and compile time +# flags. + +CC=gcc +CFLAGS=-Wall -O2 -fcommon + +################################################## +# You shouldn't need to modify anything below here +################################################## + +MISCDIR=../misc +HCL2C=$(MISCDIR)/hcl2c +INC=$(TKINC) -I$(MISCDIR) $(GUIMODE) +LIBS=$(TKLIBS) -lm +YAS = ../misc/yas + +all: psim drivers + +# This rule builds the PIPE simulator +psim: psim.c sim.h pipe-$(VERSION).hcl $(MISCDIR)/isa.c $(MISCDIR)/isa.h + # Building the pipe-$(VERSION).hcl version of PIPE + $(HCL2C) -n pipe-$(VERSION).hcl < pipe-$(VERSION).hcl > pipe-$(VERSION).c + $(CC) $(CFLAGS) $(INC) -o psim psim.c pipe-$(VERSION).c \ + $(MISCDIR)/isa.c $(LIBS) + +# This rule builds driver programs for Part C of the Architecture Lab +drivers: + ./gen-driver.pl -n 4 -f ncopy.ys > sdriver.ys + ../misc/yas sdriver.ys + ./gen-driver.pl -n 63 -f ncopy.ys > ldriver.ys + ../misc/yas ldriver.ys + +# These are implicit rules for assembling .yo files from .ys files. +.SUFFIXES: .ys .yo +.ys.yo: + $(YAS) $*.ys + + +clean: + rm -f psim pipe-*.c *.o *.exe *~ + + diff --git a/arch/sim/pipe/README b/arch/sim/pipe/README new file mode 100644 index 0000000..f7ffb5a --- /dev/null +++ b/arch/sim/pipe/README @@ -0,0 +1,150 @@ +/*********************************************************************** + * Pipelined Y86-64 Simulator + * + * Copyright (c) 2002, 2010, 2015 R. Bryant and D. O'Hallaron, + * All rights reserved. + * May not be used, modified, or copied without permission. + ***********************************************************************/ + +This directory contains the code to construct simulators for PIPE and +the variants of it described in the homework exercises. + +************************* +1. Building the simulator +************************* + +Different versions of the PIPE simulator can be constructed to use +different HCL files when working on the different homework problems. + + +Binary VERSION HCL File Description +psim std pipe-std.hcl Standard simulator (default) +psim broken pipe-broken.hcl Does not handle any hazards +psim full pipe-full.hcl For adding iaddq +psim nobypass pipe-nobypass.hcl For implementing PIPE- + (called pipe-stall.hcl in text) +psim lf pipe-lf.hcl For implementing load forwarding +psim nt pipe-nt.hcl For implementing NT branch prediction +psim btfnt pipe-btfnt.hcl For implementing BTFNT branch pred. +psim 1w pipe-1w.hcl For implementing single write port +psim super pipe-super.hcl Implements iaddq & load forwarding + +The Makefile can be configured to build simulators that support GUI +and/or TTY interfaces. A simulator running in TTY mode prints all +information about its runtime behavior on the terminal. It's hard to +understand what's going on, but useful for automated testing, and +doesn't require any special installation features. A simulator +running in GUI mode uses a fancy graphical user interface. Nice for +visualizing and debugging, but requires installation of Tcl/Tk on your +system. + +The Makefile has simple instructions for building the TTY and GUI +forms. In either case, once you've configured the Makefile, you can +build different versions of the simulators with different HCL files +with commands of the form: + + unix> make clean; make psim VERSION=xxx + +where "xxx" is one of the versions listed above. To save typing, you +can set the Makefile's VERSION variable. For example, if you are working +on Problems 4.52 and 4.53, which require to modify pipe-full.hcl, then +you could set VERSION=full in the Makefile. Typing + + unix> make clean; make psim + +would then make the pipe-full.hcl version of PIPE. + +*********************** +2. Using the simulators +*********************** + +The simulator recognizes the following command line arguments: + +Usage: psim [-htg] [-l m] [-v n] file.yo + +file.yo required in GUI mode, optional in TTY mode (default stdin) + + -h Print this message + -g Run in GUI mode instead of TTY mode (default TTY mode) + -l m Set instruction limit to m [TTY mode only] (default 10000) + -v n Set verbosity level to 0 <= n <= 2 [TTY mode only] (default 2) + -t Test result against the ISA simulator (yis) [TTY model only] + +******** +3. Files +******** + +Makefile Build the simulator +Makefile-sim Makefile for the student distribution +README This file + +********************************************** +* Files related to the CS:APP Architecture Lab +********************************************** + +* Sample programs +ncopy.ys The default version of ncopy that the students optimize +ncopy.c C version of ncopy that defines its semantics + +* Preconstructed driver programs (by gen-driver.pl) +sdriver.ys Driver that calls ncopy.ys on a short (4-word) array +ldriver.ys Driver that calls ncopy.ys on a longer (63-word) array + Both drivers are generated automatically by the + Makefile by typing "make drivers". + +* Solution files (Instructors only) +gen-ncopy.pl Generates versions of benchmark program with various + optimizations. See comments in file for explanation. + +* Testing scripts +gen-driver.pl Generate a driver program for an arbitrary ncopy + implementation (default ncopy.ys). Type "make drivers" + to construct sdriver.ys and ldriver.ys. +benchmark.pl Runs an implementation of ncopy on array sizes + 1 to 64 (default ncopy.ys) and computes its performance + in units of CPE (cycles per element). +correctness.pl Runs an implementation of ncopy on array sizes + 0 to 64, and several longer ones and checks each for + correctness. +check-len.pl Determines number of bytes in .yo representation of + ncopy function. + + +**************************************************** +* HCL files for different versions of the simulators +**************************************************** + +pipe-std.hcl The standard PIPE processor described in the text +pipe-broken.hcl A simulator that does not detect or handle hazards + (useful when explaining hazards in lectures) + +* HCL files for various CS:APP Homework Problems +pipe-nobypass.hcl 4.51: Build version of PIPE without bypassing + (called pipe-stall.hcl in the text) +pipe-full.hcl 4.52-53: Add iaddq instruction to PIPE +pipe-nt.hcl 4.54: Implement branch not taken strategy +pipe-btfnt.hcl 4.55: Implement back-taken forward-not-taken strategy +pipe-lf.hcl 4.56: Implement load forwarding logic +pipe-1w.hcl 4.57: Implement single ported register file + +* HCL solution files for the CS:APP Homework Problems (Instructors only) +pipe-nobypass-ans.hcl 4.51 solution +pipe-full-ans.hcl 4.52-53 solutions +pipe-nt-ans.hcl 4.54 solution +pipe-btfnt-ans.hcl 4.55 solution +pipe-lf-ans.hcl 4.56 solutions +pipe-1w-ans.hcl 4.57 solutions +pipe-super.hcl Gives best performance for lab + +***************************** +* PIPE simulator source files +***************************** + +psim.c Base simulator code +sim.h PIPE header files +pipeline.h +stages.h +pipe.tcl TCL script for the GUI version of PIPE + + + diff --git a/arch/sim/pipe/benchmark.pl b/arch/sim/pipe/benchmark.pl new file mode 100755 index 0000000..51a41e6 --- /dev/null +++ b/arch/sim/pipe/benchmark.pl @@ -0,0 +1,110 @@ +#!/usr/bin/perl +#!/usr/local/bin/perl + +# +# benchmark.pl - Run test of pipeline on ncopy for different block sizes +# and determine CPE (cycles per element) +# +use Getopt::Std; + +# +# Configuration +# +$blocklen = 64; +$yas = "../misc/yas"; +$pipe = "./psim"; +$gendriver = "./gen-driver.pl"; +$fname = "bdriver"; +$verbose = 1; + +## Grading criteria +$totalpoints = 60; +# What CPE is required to get full credit? +$fullcpe = 7.5; +# What CPE is required to get nonzero credit: +$threshcpe = 10.5; + + + +# +# usage - Print the help message and terminate +# +sub usage { + print STDERR "Usage: $0 [-hq] [-n N] -f FILE\n"; + print STDERR " -h Print help message\n"; + print STDERR " -q Quiet mode (default verbose)\n"; + print STDERR " -n N Set max number of elements up to 64 (default $blocklen)\n"; + print STDERR " -f FILE Input .ys file is FILE\n"; + die "\n"; +} + +getopts('hqn:f:'); + +if ($opt_h) { + usage(); +} + +if ($opt_q) { + $verbose = 0; +} + +if ($opt_n) { + $blocklen = $opt_n; + if ($blocklen < 0 || $blocklen > 64) { + print STDERR "n must be between 0 and 64\n"; + die "\n"; + } +} + +# Filename is required +if (!$opt_f) { + $ncopy = "ncopy"; +} else { + $ncopy = $opt_f; + # Strip off .ys + $ncopy =~ s/\.ys//; +} + +if ($verbose) { + print "\t$ncopy\n"; +} + +$tcpe = 0; +for ($i = 0; $i <= $blocklen; $i++) { + !(system "$gendriver -n $i -f $ncopy.ys > $fname$i.ys") || + die "Couldn't generate driver file $fname$i.ys\n"; + !(system "$yas $fname$i.ys") || + die "Couldn't assemble file $fname$i.ys\n"; + $stat = `$pipe -v 0 $fname$i.yo` || + die "Couldn't simulate file $fname$i.yo\n"; + !(system "rm $fname$i.ys $fname$i.yo") || + die "Couldn't remove files $fname$i.ys and/or $fname$i.yo\n"; + chomp $stat; + $stat =~ s/[ ]*CPI:[ ]*//; + $stat =~ s/ cycles.*//; + if ($i > 0) { + $cpe = $stat/$i; + if ($verbose) { + printf "%d\t%d\t%.2f\n", $i, $stat, $cpe; + } + $tcpe += $cpe; + } else { + if ($verbose) { + printf "%d\t%d\n", $i, $stat; + } + } + +} + +$acpe = $tcpe/$blocklen; +printf "Average CPE\t%.2f\n", $acpe; + +## Compute Score +$score = 0; +if ($acpe <= $fullcpe) { + $score = $totalpoints; +} elsif ($acpe <= $threshcpe) { + $score = $totalpoints * ($threshcpe - $acpe)/($threshcpe - $fullcpe); +} +printf "Score\t%.1f/%.1f\n", $score, $totalpoints; + diff --git a/arch/sim/pipe/check-len.pl b/arch/sim/pipe/check-len.pl new file mode 100755 index 0000000..63adff5 --- /dev/null +++ b/arch/sim/pipe/check-len.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +# Check length of ncopy function in .yo file +# Assumes that function starts with label "ncopy:" +# and finishes with label "End:" + +$startpos = -1; +$endpos = -1; + +while (<>) { + $line = $_; + if ($line =~ /(0x[0-9a-fA-F]+):.* ncopy:/) { + $startpos = hex($1); + } + if ($line =~ /(0x[0-9a-fA-F]+):.* End:/) { + $endpos = hex($1); + } +} + +if ($startpos >= 0 && $endpos > $startpos) { + $len = $endpos - $startpos; + print "ncopy length = $len bytes\n"; +} else { + print "Couldn't determine ncopy length\n"; +} diff --git a/arch/sim/pipe/correctness.pl b/arch/sim/pipe/correctness.pl new file mode 100755 index 0000000..bba36ca --- /dev/null +++ b/arch/sim/pipe/correctness.pl @@ -0,0 +1,139 @@ +#!/usr/bin/perl +#!/usr/local/bin/perl + +# +# correctness.pl - Test ncopy assembly code for correctness +# +use Getopt::Std; + +# +# Configuration +# +$blocklen = 64; +$over = 3; +$yas = "../misc/yas"; +$yis = "../misc/yis"; +$pipe = "./psim"; +$gendriver = "./gen-driver.pl"; +$fname = "cdriver"; +$verbose = 1; +# Maximum allowable code length +$bytelim = 1000; + +# +# usage - Print the help message and terminate +# +sub usage { + print STDERR "Usage: $0 [-hqp] [-n N] -f FILE\n"; + print STDERR " -h Print help message\n"; + print STDERR " -q Quiet mode (default verbose)\n"; + print STDERR " -p Run program on pipeline simulator (default ISA sim)\n"; + print STDERR " -n N Set max number of elements up to 64 (default $blocklen)\n"; + print STDERR " -f FILE Input .ys file is FILE\n"; + print STDERR " -b blim set byte limit for function\n"; + die "\n"; +} + +getopts('hqpn:f:b:'); + +if ($opt_h) { + usage(); +} + +if ($opt_q) { + $verbose = 0; +} + +if ($opt_b) { + $bytelim = $opt_b; +} + +$usepipe = 0; +if ($opt_p) { + $usepipe = 1; + print "Simulating with pipeline simulator psim\n"; +} else { + print "Simulating with instruction set simulator yis\n"; +} + + +if ($opt_n) { + $blocklen = $opt_n; + if ($blocklen < 0) { + print STDERR "n must be >= 0\n"; + die "\n"; + } +} + +# Filename is required +if (!$opt_f) { + $ncopy = "ncopy"; +} else { + $ncopy = $opt_f; + # Strip off .ys + $ncopy =~ s/\.ys//; +} + +if ($verbose) { + print "\t$ncopy\n"; +} + +$goodcnt = 0; + +for ($i = 0; $i <= $blocklen+$over; $i++) { + $len = $i; + if ($i > $blocklen) { + # Try some larger values + $len = $blocklen * ($i - $blocklen + 1); + } + !(system "$gendriver -rc -n $len -f $ncopy.ys -b $bytelim > $fname$len.ys") || + die "Couldn't generate driver file $fname$len.ys\n"; + !(system "$yas $fname$len.ys") || + die "Couldn't assemble file $fname$len.ys\n"; + if ($usepipe) { + !(system "$pipe -v 1 $fname$len.yo > $fname$len.pipe") || + die "Couldn't simulate file $fname$len.yo with pipeline simulator\n"; + $stat = `grep "rax:" $fname$len.pipe`; + !(system "rm $fname$len.ys $fname$len.yo $fname$len.pipe") || + die "Couldn't remove files $fname$len.ys and/or $fname$len.yo and/or $fname$len.pipe\n"; + chomp $stat; + } else { + !(system "$yis $fname$len.yo > $fname$len.yis") || + die "Couldn't simulate file $fname$len.yo with instruction set simulator\n"; + $stat = `grep rax $fname$len.yis`; + !(system "rm $fname$len.ys $fname$len.yo $fname$len.yis") || + die "Couldn't remove files $fname$len.ys and/or $fname$len.yo and/or $fname$len.yis\n"; + chomp $stat; + } + $result = "failed"; + if ($stat =~ "zzzz") { + $result = "Couldn't run checking code"; + } + if ($stat =~ "aaaa") { + $result = "OK"; + $goodcnt ++; + } + if ($stat =~ "bbbb") { + $result = "Bad count"; + } + if ($stat =~ "cccc") { + $result = "Program too long"; + printf "%d\t%s\n", $len, $result; + last; + } + if ($stat =~ "dddd") { + $result = "Incorrect copying"; + } + if ($stat =~ "eeee") { + $result = "Corruption before or after destination"; + } + if ($verbose) { + printf "%d\t%s\n", $len, $result; + } +} + +$bp1 = $blocklen+$over+1; +printf "$goodcnt/$bp1 pass correctness test\n"; + + + diff --git a/arch/sim/pipe/gen-driver.pl b/arch/sim/pipe/gen-driver.pl new file mode 100755 index 0000000..d2fff16 --- /dev/null +++ b/arch/sim/pipe/gen-driver.pl @@ -0,0 +1,247 @@ +#!/usr/bin/perl +#!/usr/local/bin/perl + +# +# gen-driver - Generate driver file for any ncopy function +# +use Getopt::Std; + +$n = 0; + +getopts('hcrn:f:b:'); + +if ($opt_h) { + print STDERR "Usage $argv[0] [-h] [-c] [-n N] [-f FILE]\n"; + print STDERR " -h print help message\n"; + print STDERR " -c include correctness checking code\n"; + print STDERR " -n N set number of elements\n"; + print STDERR " -f FILE set input file (default stdin)\n"; + print STDERR " -b blim set byte limit for function\n"; + print STDERR " -r Allow random result\n"; + die "\n"; +} + +$check = 0; +if ($opt_c) { + $check = 1; +} + +$bytelim = 1000; +if ($opt_b) { + $bytelim = $opt_b; +} + +if ($opt_n) { + $n = $opt_n; + if ($n < 0) { + print STDERR "n must be at least 0\n"; + die "\n"; + } +} + +$randomval = 0; +# Accumulated count +$rval = 0; + +if ($opt_r) { + $randomval = 1; +} else { + # Value that should be returned by function + $tval = int($n/2); +} + + +# The data to be stored. +@data = (); + +for ($i = 0; $i < $n; $i++) { + $data[$i] = -($i+1); + if ($randomval) { + if (int(rand(2)) == 1) { + $data[$i] = -$data[$i]; + $rval++; + } + } else { + if ($rval < $tval && int(rand(2)) % 2 == 1 || + $tval - $rval >= $n - $i) { + $data[$i] = -$data[$i]; + $rval++; + } + } +} + + +# Values to put at beginning and end of destination +$Preval = "0xbcdefa"; +$Postval = "0xdefabc"; + + +print <) { + printf "%s", $_; + } +} else { + while (<>) { + printf "%s", $_; + } +} +print "EndFun:\n"; + +if ($check) { +print <0) contained in src. +# +# Include your name and ID here. +# +# Describe how and why you modified the baseline code. +# +################################################################## +# Do not modify this portion +# Function prologue. +# %rdi = src, %rsi = dst, %rdx = len +ncopy: + +################################################################## +# You can modify this portion + # Loop header + xorq %rax,%rax # count = 0; + iaddq $0xfffffffffffffffc, %rdx + jle Tail # if so, goto Done: + +Loop1: + mrmovq (%rdi), %r10 # read val from src... + rmmovq %r10, (%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos1 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos1: + + mrmovq 8(%rdi), %r10 # read val from src... + rmmovq %r10, 8(%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos2 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos2: + + mrmovq 16(%rdi), %r10 # read val from src... + rmmovq %r10, 16(%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos3 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos3: + + mrmovq 24(%rdi), %r10 # read val from src... + rmmovq %r10, 24(%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos4 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos4: + + mrmovq 32(%rdi), %r10 # read val from src... + rmmovq %r10, 32(%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos5 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos5: + + iaddq $40, %rdi # src++ + iaddq $40, %rsi # dst++ + iaddq $0xfffffffffffffffb, %rdx # len-- + jg Loop1 + +Tail: + iaddq $4, %rdx + jle Done +Loop2: + mrmovq (%rdi), %r10 + rmmovq %r10, (%rsi) + andq %r10, %r10 + jle NposT + iaddq $1, %rax +NposT: + iaddq $8, %rdi + iaddq $8, %rsi + iaddq $0xffffffffffffffff, %rdx + jg Loop2 + + +################################################################## +# Do not modify the following section of code +# Function epilogue. +Done: + ret +################################################################## +# Keep the following label at the end of your function +End: +#/* $end ncopy-ys */ +EndFun: + +############################### +# Source and destination blocks +############################### + .align 8 +src: + .quad 1 + .quad 2 + .quad -3 + .quad -4 + .quad -5 + .quad 6 + .quad -7 + .quad 8 + .quad 9 + .quad -10 + .quad 11 + .quad 12 + .quad -13 + .quad 14 + .quad -15 + .quad 16 + .quad -17 + .quad -18 + .quad 19 + .quad -20 + .quad 21 + .quad -22 + .quad -23 + .quad -24 + .quad -25 + .quad -26 + .quad 27 + .quad -28 + .quad -29 + .quad -30 + .quad 31 + .quad -32 + .quad 33 + .quad -34 + .quad 35 + .quad 36 + .quad -37 + .quad 38 + .quad 39 + .quad 40 + .quad 41 + .quad -42 + .quad 43 + .quad 44 + .quad 45 + .quad 46 + .quad 47 + .quad 48 + .quad 49 + .quad 50 + .quad 51 + .quad -52 + .quad -53 + .quad 54 + .quad -55 + .quad -56 + .quad 57 + .quad -58 + .quad -59 + .quad -60 + .quad -61 + .quad -62 + .quad -63 + .quad 0xbcdefa # This shouldn't get moved + + .align 16 +Predest: + .quad 0xbcdefa +dest: + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab +Postdest: + .quad 0xdefabc + +.align 8 +# Run time stack + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + +Stack: diff --git a/arch/sim/pipe/ncopy.c b/arch/sim/pipe/ncopy.c new file mode 100644 index 0000000..a576f8e --- /dev/null +++ b/arch/sim/pipe/ncopy.c @@ -0,0 +1,39 @@ +#include + +typedef word_t word_t; + +word_t src[8], dst[8]; + +/* $begin ncopy */ +/* + * ncopy - copy src to dst, returning number of positive ints + * contained in src array. + */ +word_t ncopy(word_t *src, word_t *dst, word_t len) +{ + word_t count = 0; + word_t val; + + while (len > 0) { + val = *src++; + *dst++ = val; + if (val > 0) + count++; + len--; + } + return count; +} +/* $end ncopy */ + +int main() +{ + word_t i, count; + + for (i=0; i<8; i++) + src[i]= i+1; + count = ncopy(src, dst, 8); + printf ("count=%d\n", count); + exit(0); +} + + diff --git a/arch/sim/pipe/ncopy.ys b/arch/sim/pipe/ncopy.ys new file mode 100644 index 0000000..08aa9f1 --- /dev/null +++ b/arch/sim/pipe/ncopy.ys @@ -0,0 +1,183 @@ +#/* $begin ncopy-ys */ +################################################################## +# ncopy.ys - Copy a src block of len words to dst. +# Return the number of positive words (>0) contained in src. +# +# Include your name and ID here. +# +# Describe how and why you modified the baseline code. +# +################################################################## +# Do not modify this portion +# Function prologue. +# %rdi = src, %rsi = dst, %rdx = len +ncopy: + +################################################################## +# You can modify this portion + + xorq %rax, %rax + iaddq $0xfffffffffffffff7, %rdx + jle Tail + +Loop: + mrmovq (%rdi), %r10 + rmmovq %r10, (%rsi) + andq %r10, %r10 + jle Npos1 + iaddq $1, %rax +Npos1: + + mrmovq 8(%rdi), %r10 + rmmovq %r10, 8(%rsi) + andq %r10, %r10 + jle Npos2 + iaddq $1, %rax +Npos2: + + mrmovq 16(%rdi), %r10 + rmmovq %r10, 16(%rsi) + andq %r10, %r10 + jle Npos3 + iaddq $1, %rax +Npos3: + + mrmovq 24(%rdi), %r10 + rmmovq %r10, 24(%rsi) + andq %r10, %r10 + jle Npos4 + iaddq $1, %rax +Npos4: + + mrmovq 32(%rdi), %r10 + rmmovq %r10, 32(%rsi) + andq %r10, %r10 + jle Npos5 + iaddq $1, %rax +Npos5: + + mrmovq 40(%rdi), %r10 + rmmovq %r10, 40(%rsi) + andq %r10, %r10 + jle Npos6 + iaddq $1, %rax +Npos6: + + mrmovq 48(%rdi), %r10 + rmmovq %r10, 48(%rsi) + andq %r10, %r10 + jle Npos7 + iaddq $1, %rax +Npos7: + + mrmovq 56(%rdi), %r10 + rmmovq %r10, 56(%rsi) + andq %r10, %r10 + jle Npos8 + iaddq $1, %rax +Npos8: + + mrmovq 64(%rdi), %r10 + rmmovq %r10, 64(%rsi) + andq %r10, %r10 + jle Npos9 + iaddq $1, %rax +Npos9: + + mrmovq 72(%rdi), %r10 + rmmovq %r10, 72(%rsi) + andq %r10, %r10 + jle Npos10 + iaddq $1, %rax +Npos10: + + iaddq $80, %rdi + iaddq $80, %rsi + iaddq $0xfffffffffffffff6, %rdx + jg Loop + +Tail: + addq %rdx, %rdx + addq %rdx, %rdx + addq %rdx, %rdx + mrmovq JT(%rdx), %rdx + pushq %rdx + ret + + .quad Done + .quad J1 + .quad J2 + .quad J3 + .quad J4 + .quad J5 + .quad J6 + .quad J7 + .quad J8 +JT: + .quad J9 + +J9: + mrmovq 64(%rdi), %r10 + rmmovq %r10, 64(%rsi) + andq %r10, %r10 + jle J8 + iaddq $1, %rax +J8: + mrmovq 56(%rdi), %r10 + rmmovq %r10, 56(%rsi) + andq %r10, %r10 + jle J7 + iaddq $1, %rax +J7: + mrmovq 48(%rdi), %r10 + rmmovq %r10, 48(%rsi) + andq %r10, %r10 + jle J6 + iaddq $1, %rax +J6: + mrmovq 40(%rdi), %r10 + rmmovq %r10, 40(%rsi) + andq %r10, %r10 + jle J5 + iaddq $1, %rax +J5: + mrmovq 32(%rdi), %r10 + rmmovq %r10, 32(%rsi) + andq %r10, %r10 + jle J4 + iaddq $1, %rax +J4: + mrmovq 24(%rdi), %r10 + rmmovq %r10, 24(%rsi) + andq %r10, %r10 + jle J3 + iaddq $1, %rax +J3: + mrmovq 16(%rdi), %r10 + rmmovq %r10, 16(%rsi) + andq %r10, %r10 + jle J2 + iaddq $1, %rax +J2: + mrmovq 8(%rdi), %r10 + rmmovq %r10, 8(%rsi) + andq %r10, %r10 + jle J1 + iaddq $1, %rax +J1: + mrmovq (%rdi), %r10 + rmmovq %r10, (%rsi) + andq %r10, %r10 + jle Done + iaddq $1, %rax + +################################################################## +# Do not modify the following section of code +# Function epilogue. +Done: + ret + +################################################################## +# Keep the following label at the end of your function +End: +#/* $end ncopy-ys */ diff --git a/arch/sim/pipe/pipe-1w.hcl b/arch/sim/pipe/pipe-1w.hcl new file mode 100644 index 0000000..f6d5daa --- /dev/null +++ b/arch/sim/pipe/pipe-1w.hcl @@ -0,0 +1,395 @@ +#/* $begin pipe-all-hcl */ +#################################################################### +# HCL Description of Control for Pipelined Y86-64 Processor # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2014 # +#################################################################### + +## Your task is to modify the design so that on any cycle, only +## one of the two possible (valE and valM) register writes will occur. +## This requires special handling of the popq instruction. +## Overall strategy: IPOPQ passes through pipe, +## treated as stack pointer increment, but not incrementing the PC +## On refetch, modify fetched icode to indicate an instruction "IPOP2", +## which reads from memory. +## This requires modifying the definition of f_icode +## and lots of other changes. Relevant positions to change +## are indicated by comments starting with keyword "1W". + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "pipeline.h"' +quote '#include "stages.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'int main(int argc, char *argv[]){return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' +# 1W: Special instruction code for second try of popq +wordsig IPOP2 'I_POP2' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ########################## +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SBUB 'STAT_BUB' # Bubble in stage +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic ############## + +##### Pipeline Register F ########################################## + +wordsig F_predPC 'pc_curr->pc' # Predicted value of PC + +##### Intermediate Values in Fetch Stage ########################### + +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig f_icode 'if_id_next->icode' # (Possibly modified) instruction code +wordsig f_ifun 'if_id_next->ifun' # Fetched instruction function +wordsig f_valC 'if_id_next->valc' # Constant data of fetched instruction +wordsig f_valP 'if_id_next->valp' # Address of following instruction +## 1W: Provide access to the PC value for the current instruction +wordsig f_pc 'f_pc' # Address of fetched instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Pipeline Register D ########################################## +wordsig D_icode 'if_id_curr->icode' # Instruction code +wordsig D_rA 'if_id_curr->ra' # rA field from instruction +wordsig D_rB 'if_id_curr->rb' # rB field from instruction +wordsig D_valP 'if_id_curr->valp' # Incremented PC + +##### Intermediate Values in Decode Stage ######################### + +wordsig d_srcA 'id_ex_next->srca' # srcA from decoded instruction +wordsig d_srcB 'id_ex_next->srcb' # srcB from decoded instruction +wordsig d_rvalA 'd_regvala' # valA read from register file +wordsig d_rvalB 'd_regvalb' # valB read from register file + +##### Pipeline Register E ########################################## +wordsig E_icode 'id_ex_curr->icode' # Instruction code +wordsig E_ifun 'id_ex_curr->ifun' # Instruction function +wordsig E_valC 'id_ex_curr->valc' # Constant data +wordsig E_srcA 'id_ex_curr->srca' # Source A register ID +wordsig E_valA 'id_ex_curr->vala' # Source A value +wordsig E_srcB 'id_ex_curr->srcb' # Source B register ID +wordsig E_valB 'id_ex_curr->valb' # Source B value +wordsig E_dstE 'id_ex_curr->deste' # Destination E register ID +wordsig E_dstM 'id_ex_curr->destm' # Destination M register ID + +##### Intermediate Values in Execute Stage ######################### +wordsig e_valE 'ex_mem_next->vale' # valE generated by ALU +boolsig e_Cnd 'ex_mem_next->takebranch' # Does condition hold? +wordsig e_dstE 'ex_mem_next->deste' # dstE (possibly modified to be RNONE) + +##### Pipeline Register M ######################### +wordsig M_stat 'ex_mem_curr->status' # Instruction status +wordsig M_icode 'ex_mem_curr->icode' # Instruction code +wordsig M_ifun 'ex_mem_curr->ifun' # Instruction function +wordsig M_valA 'ex_mem_curr->vala' # Source A value +wordsig M_dstE 'ex_mem_curr->deste' # Destination E register ID +wordsig M_valE 'ex_mem_curr->vale' # ALU E value +wordsig M_dstM 'ex_mem_curr->destm' # Destination M register ID +boolsig M_Cnd 'ex_mem_curr->takebranch' # Condition flag +boolsig dmem_error 'dmem_error' # Error signal from instruction memory + +##### Intermediate Values in Memory Stage ########################## +wordsig m_valM 'mem_wb_next->valm' # valM generated by memory +wordsig m_stat 'mem_wb_next->status' # stat (possibly modified to be SADR) + +##### Pipeline Register W ########################################## +wordsig W_stat 'mem_wb_curr->status' # Instruction status +wordsig W_icode 'mem_wb_curr->icode' # Instruction code +wordsig W_dstE 'mem_wb_curr->deste' # Destination E register ID +wordsig W_valE 'mem_wb_curr->vale' # ALU E value +wordsig W_dstM 'mem_wb_curr->destm' # Destination M register ID +wordsig W_valM 'mem_wb_curr->valm' # Memory M value + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +## What address should instruction be fetched at +word f_pc = [ + # Mispredicted branch. Fetch at incremented PC + M_icode == IJXX && !M_Cnd : M_valA; + # Completion of RET instruction + W_icode == IRET : W_valM; + # Default: Use predicted value of PC + 1 : F_predPC; +]; + +## Determine icode of fetched instruction +## 1W: To split ipopq into two cycles, need to be able to +## modify value of icode, +## so that it will be IPOP2 when fetched for second time. +word f_icode = [ + imem_error : INOP; + D_icode == IPOPQ : IPOP2; + 1: imem_icode; +]; + +# Determine ifun +word f_ifun = [ + imem_error : FNONE; + 1: imem_ifun; +]; + +# Is instruction valid? +bool instr_valid = f_icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ, IPOP2 }; + +# Determine status code for fetched instruction +word f_stat = [ + imem_error: SADR; + !instr_valid : SINS; + f_icode == IHALT : SHLT; + 1 : SAOK; +]; + +# Does fetched instruction require a regid byte? +bool need_regids = + f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOP2, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +# Predict next value of PC +word f_predPC = [ + f_icode in { IJXX, ICALL } : f_valC; + ## 1W: Want to refetch popq one time + f_icode == IPOPQ : F_predPC; + 1 : f_valP; +]; + +################ Decode Stage ###################################### + +## W1: Strategy. Decoding of popq rA should be treated the same +## as would iaddq $8, %rsp +## Decoding of pop2 rA treated same as mrmovq -8(%rsp), rA + +## What register should be used as the A source? +word d_srcA = [ + D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ, IPOP2 } : D_rA; + D_icode == IRET : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word d_srcB = [ + D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, IPOP2, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word d_dstE = [ + D_icode in { IRRMOVQ, IIRMOVQ, IOPQ} : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word d_dstM = [ + D_icode in { IMRMOVQ, IPOP2 } : D_rA; + 1 : RNONE; # Don't write any register +]; + +## What should be the A value? +## Forward into decode stage for valA +word d_valA = [ + D_icode in { ICALL, IJXX } : D_valP; # Use incremented PC + d_srcA == e_dstE : e_valE; # Forward valE from execute + d_srcA == M_dstM : m_valM; # Forward valM from memory + d_srcA == M_dstE : M_valE; # Forward valE from memory + d_srcA == W_dstM : W_valM; # Forward valM from write back + d_srcA == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalA; # Use value read from register file +]; + +word d_valB = [ + d_srcB == e_dstE : e_valE; # Forward valE from execute + d_srcB == M_dstM : m_valM; # Forward valM from memory + d_srcB == M_dstE : M_valE; # Forward valE from memory + d_srcB == W_dstM : W_valM; # Forward valM from write back + d_srcB == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalB; # Use value read from register file +]; + +################ Execute Stage ##################################### + +## Select input A to ALU +word aluA = [ + E_icode in { IRRMOVQ, IOPQ } : E_valA; + E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : E_valC; + E_icode in { ICALL, IPUSHQ, IPOP2 } : -8; + E_icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ, IPOP2 } : E_valB; + E_icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + E_icode == IOPQ : E_ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = E_icode == IOPQ && + # State changes only during normal operation + !m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT }; + +## Generate valA in execute stage +word e_valA = E_valA; # Pass valA through stage + +## Set dstE to RNONE in event of not-taken conditional move +word e_dstE = [ + E_icode == IRRMOVQ && !e_Cnd : RNONE; + 1 : E_dstE; +]; + +################ Memory Stage ###################################### + +## Select memory address +word mem_addr = [ + M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ, IPOP2 } : M_valE; + M_icode == IRET : M_valA; + # Other instructions don't need address +]; + +## Set read control signal +bool mem_read = M_icode in { IMRMOVQ, IRET, IPOP2 }; + +## Set write control signal +bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL }; + +#/* $begin pipe-m_stat-hcl */ +## Update the status +word m_stat = [ + dmem_error : SADR; + 1 : M_stat; +]; +#/* $end pipe-m_stat-hcl */ + +################ Write back stage ################################## + +## 1W: For this problem, we introduce a multiplexor that merges +## valE and valM into a single value for writing to register port E. +## DO NOT CHANGE THIS LOGIC +## Merge both write back sources onto register port E +## Set E port register ID +word w_dstE = [ + ## writing from valM + W_dstM != RNONE : W_dstM; + 1: W_dstE; +]; + +## Set E port value +word w_valE = [ + W_dstM != RNONE : W_valM; + 1: W_valE; +]; + +## Disable register port M +## Set M port register ID +word w_dstM = RNONE; + +## Set M port value +word w_valM = 0; + +## Update processor status +word Stat = [ + W_stat == SBUB : SAOK; + 1 : W_stat; +]; + +################ Pipeline Register Control ######################### + +# Should I stall or inject a bubble into Pipeline Register F? +# At most one of these can be true. +bool F_bubble = 0; +bool F_stall = + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOP2 } && + E_dstM in { d_srcA, d_srcB } || + # Stalling at fetch while ret passes through pipeline + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register D? +# At most one of these can be true. +bool D_stall = + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOP2 } && + E_dstM in { d_srcA, d_srcB }; + +bool D_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Stalling at fetch while ret passes through pipeline + # but not condition for a load/use hazard + !(E_icode in { IMRMOVQ, IPOP2 } && E_dstM in { d_srcA, d_srcB }) && + # 1W: This condition will change + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register E? +# At most one of these can be true. +bool E_stall = 0; +bool E_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOP2 } && + E_dstM in { d_srcA, d_srcB}; + +# Should I stall or inject a bubble into Pipeline Register M? +# At most one of these can be true. +bool M_stall = 0; +# Start injecting bubbles as soon as exception passes through memory stage +bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT }; + +# Should I stall or inject a bubble into Pipeline Register W? +bool W_stall = W_stat in { SADR, SINS, SHLT }; +bool W_bubble = 0; +#/* $end pipe-all-hcl */ diff --git a/arch/sim/pipe/pipe-broken.hcl b/arch/sim/pipe/pipe-broken.hcl new file mode 100644 index 0000000..eac6a61 --- /dev/null +++ b/arch/sim/pipe/pipe-broken.hcl @@ -0,0 +1,331 @@ +#/* $begin pipe-all-hcl */ +#################################################################### +# HCL Description of Control for Pipelined Y86-64 Processor # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2014 # +#################################################################### +## This version does not detect or handle any hazards + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "pipeline.h"' +quote '#include "stages.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'int main(int argc, char *argv[]){return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ########################## +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SBUB 'STAT_BUB' # Bubble in stage +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic ############## + +##### Pipeline Register F ########################################## + +wordsig F_predPC 'pc_curr->pc' # Predicted value of PC + +##### Intermediate Values in Fetch Stage ########################### + +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig f_icode 'if_id_next->icode' # (Possibly modified) instruction code +wordsig f_ifun 'if_id_next->ifun' # Fetched instruction function +wordsig f_valC 'if_id_next->valc' # Constant data of fetched instruction +wordsig f_valP 'if_id_next->valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Pipeline Register D ########################################## +wordsig D_icode 'if_id_curr->icode' # Instruction code +wordsig D_rA 'if_id_curr->ra' # rA field from instruction +wordsig D_rB 'if_id_curr->rb' # rB field from instruction +wordsig D_valP 'if_id_curr->valp' # Incremented PC + +##### Intermediate Values in Decode Stage ######################### + +wordsig d_srcA 'id_ex_next->srca' # srcA from decoded instruction +wordsig d_srcB 'id_ex_next->srcb' # srcB from decoded instruction +wordsig d_rvalA 'd_regvala' # valA read from register file +wordsig d_rvalB 'd_regvalb' # valB read from register file + +##### Pipeline Register E ########################################## +wordsig E_icode 'id_ex_curr->icode' # Instruction code +wordsig E_ifun 'id_ex_curr->ifun' # Instruction function +wordsig E_valC 'id_ex_curr->valc' # Constant data +wordsig E_srcA 'id_ex_curr->srca' # Source A register ID +wordsig E_valA 'id_ex_curr->vala' # Source A value +wordsig E_srcB 'id_ex_curr->srcb' # Source B register ID +wordsig E_valB 'id_ex_curr->valb' # Source B value +wordsig E_dstE 'id_ex_curr->deste' # Destination E register ID +wordsig E_dstM 'id_ex_curr->destm' # Destination M register ID + +##### Intermediate Values in Execute Stage ######################### +wordsig e_valE 'ex_mem_next->vale' # valE generated by ALU +boolsig e_Cnd 'ex_mem_next->takebranch' # Does condition hold? +wordsig e_dstE 'ex_mem_next->deste' # dstE (possibly modified to be RNONE) + +##### Pipeline Register M ######################### +wordsig M_stat 'ex_mem_curr->status' # Instruction status +wordsig M_icode 'ex_mem_curr->icode' # Instruction code +wordsig M_ifun 'ex_mem_curr->ifun' # Instruction function +wordsig M_valA 'ex_mem_curr->vala' # Source A value +wordsig M_dstE 'ex_mem_curr->deste' # Destination E register ID +wordsig M_valE 'ex_mem_curr->vale' # ALU E value +wordsig M_dstM 'ex_mem_curr->destm' # Destination M register ID +boolsig M_Cnd 'ex_mem_curr->takebranch' # Condition flag +boolsig dmem_error 'dmem_error' # Error signal from instruction memory + +##### Intermediate Values in Memory Stage ########################## +wordsig m_valM 'mem_wb_next->valm' # valM generated by memory +wordsig m_stat 'mem_wb_next->status' # stat (possibly modified to be SADR) + +##### Pipeline Register W ########################################## +wordsig W_stat 'mem_wb_curr->status' # Instruction status +wordsig W_icode 'mem_wb_curr->icode' # Instruction code +wordsig W_dstE 'mem_wb_curr->deste' # Destination E register ID +wordsig W_valE 'mem_wb_curr->vale' # ALU E value +wordsig W_dstM 'mem_wb_curr->destm' # Destination M register ID +wordsig W_valM 'mem_wb_curr->valm' # Memory M value + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +## What address should instruction be fetched at +word f_pc = [ + # Mispredicted branch. Fetch at incremented PC + M_icode == IJXX && !M_Cnd : M_valA; + # Completion of RET instruction + W_icode == IRET : W_valM; + # Default: Use predicted value of PC + 1 : F_predPC; +]; + +## Determine icode of fetched instruction +word f_icode = [ + imem_error : INOP; + 1: imem_icode; +]; + +# Determine ifun +word f_ifun = [ + imem_error : FNONE; + 1: imem_ifun; +]; + +# Is instruction valid? +bool instr_valid = f_icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ }; + +# Determine status code for fetched instruction +word f_stat = [ + imem_error: SADR; + !instr_valid : SINS; + f_icode == IHALT : SHLT; + 1 : SAOK; +]; + +# Does fetched instruction require a regid byte? +bool need_regids = + f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +# Predict next value of PC +word f_predPC = [ + f_icode in { IJXX, ICALL } : f_valC; + 1 : f_valP; +]; + +################ Decode Stage ###################################### + + +## What register should be used as the A source? +word d_srcA = [ + D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA; + D_icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word d_srcB = [ + D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word d_dstE = [ + D_icode in { IRRMOVQ, IIRMOVQ, IOPQ} : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word d_dstM = [ + D_icode in { IMRMOVQ, IPOPQ } : D_rA; + 1 : RNONE; # Don't write any register +]; + +## What should be the A value? +## No forwarding. valA is either valP or value from register file +word d_valA = [ + D_icode in { ICALL, IJXX } : D_valP; # Use incremented PC + 1 : d_rvalA; # Use value read from register file +]; + +## No forwarding. valB is value from register file +word d_valB = d_rvalB; + +################ Execute Stage ##################################### + +## Select input A to ALU +word aluA = [ + E_icode in { IRRMOVQ, IOPQ } : E_valA; + E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : E_valC; + E_icode in { ICALL, IPUSHQ } : -8; + E_icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ } : E_valB; + E_icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + E_icode == IOPQ : E_ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = E_icode == IOPQ && + # State changes only during normal operation + !m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT }; + +## Generate valA in execute stage +word e_valA = E_valA; # Pass valA through stage + +## Set dstE to RNONE in event of not-taken conditional move +word e_dstE = [ + E_icode == IRRMOVQ && !e_Cnd : RNONE; + 1 : E_dstE; +]; + +################ Memory Stage ###################################### + +## Select memory address +word mem_addr = [ + M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : M_valE; + M_icode in { IPOPQ, IRET } : M_valA; + # Other instructions don't need address +]; + +## Set read control signal +bool mem_read = M_icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL }; + +#/* $begin pipe-m_stat-hcl */ +## Update the status +word m_stat = [ + dmem_error : SADR; + 1 : M_stat; +]; +#/* $end pipe-m_stat-hcl */ + +## Set E port register ID +word w_dstE = W_dstE; + +## Set E port value +word w_valE = W_valE; + +## Set M port register ID +word w_dstM = W_dstM; + +## Set M port value +word w_valM = W_valM; + +## Update processor status +word Stat = [ + W_stat == SBUB : SAOK; + 1 : W_stat; +]; + +################ Pipeline Register Control ######################### + +# Should I stall or inject a bubble into Pipeline Register F? +# At most one of these can be true. +bool F_bubble = 0; +bool F_stall = + 0; + +# Should I stall or inject a bubble into Pipeline Register D? +# At most one of these can be true. +bool D_stall = + 0; + +bool D_bubble = + 0; + +# Should I stall or inject a bubble into Pipeline Register E? +# At most one of these can be true. +bool E_stall = 0; +bool E_bubble = + 0; + +# Should I stall or inject a bubble into Pipeline Register M? +# At most one of these can be true. +bool M_stall = 0; +# Start injecting bubbles as soon as exception passes through memory stage +bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT }; + +# Should I stall or inject a bubble into Pipeline Register W? +bool W_stall = W_stat in { SADR, SINS, SHLT }; +bool W_bubble = 0; +#/* $end pipe-all-hcl */ diff --git a/arch/sim/pipe/pipe-btfnt.hcl b/arch/sim/pipe/pipe-btfnt.hcl new file mode 100644 index 0000000..51f47c9 --- /dev/null +++ b/arch/sim/pipe/pipe-btfnt.hcl @@ -0,0 +1,380 @@ +#/* $begin pipe-all-hcl */ +#################################################################### +# HCL Description of Control for Pipelined Y86-64 Processor # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2014 # +#################################################################### + +## Your task is to modify the design so that conditional branches are +## predicted as being taken when backward and not-taken when forward +## The code here is nearly identical to that for the normal pipeline. +## Comments starting with keyword "BBTFNT" have been added at places +## relevant to the exercise. + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "pipeline.h"' +quote '#include "stages.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'int main(int argc, char *argv[]){return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ########################## +wordsig ALUADD 'A_ADD' # ALU should add its arguments +## BBTFNT: For modified branch prediction, need to distinguish +## conditional vs. unconditional branches +##### Jump conditions referenced explicitly +wordsig UNCOND 'C_YES' # Unconditional transfer + +##### Possible instruction status values ##### +wordsig SBUB 'STAT_BUB' # Bubble in stage +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic ############## + +##### Pipeline Register F ########################################## + +wordsig F_predPC 'pc_curr->pc' # Predicted value of PC + +##### Intermediate Values in Fetch Stage ########################### + +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig f_icode 'if_id_next->icode' # (Possibly modified) instruction code +wordsig f_ifun 'if_id_next->ifun' # Fetched instruction function +wordsig f_valC 'if_id_next->valc' # Constant data of fetched instruction +wordsig f_valP 'if_id_next->valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Pipeline Register D ########################################## +wordsig D_icode 'if_id_curr->icode' # Instruction code +wordsig D_rA 'if_id_curr->ra' # rA field from instruction +wordsig D_rB 'if_id_curr->rb' # rB field from instruction +wordsig D_valP 'if_id_curr->valp' # Incremented PC + +##### Intermediate Values in Decode Stage ######################### + +wordsig d_srcA 'id_ex_next->srca' # srcA from decoded instruction +wordsig d_srcB 'id_ex_next->srcb' # srcB from decoded instruction +wordsig d_rvalA 'd_regvala' # valA read from register file +wordsig d_rvalB 'd_regvalb' # valB read from register file + +##### Pipeline Register E ########################################## +wordsig E_icode 'id_ex_curr->icode' # Instruction code +wordsig E_ifun 'id_ex_curr->ifun' # Instruction function +wordsig E_valC 'id_ex_curr->valc' # Constant data +wordsig E_srcA 'id_ex_curr->srca' # Source A register ID +wordsig E_valA 'id_ex_curr->vala' # Source A value +wordsig E_srcB 'id_ex_curr->srcb' # Source B register ID +wordsig E_valB 'id_ex_curr->valb' # Source B value +wordsig E_dstE 'id_ex_curr->deste' # Destination E register ID +wordsig E_dstM 'id_ex_curr->destm' # Destination M register ID + +##### Intermediate Values in Execute Stage ######################### +wordsig e_valE 'ex_mem_next->vale' # valE generated by ALU +boolsig e_Cnd 'ex_mem_next->takebranch' # Does condition hold? +wordsig e_dstE 'ex_mem_next->deste' # dstE (possibly modified to be RNONE) + +##### Pipeline Register M ######################### +wordsig M_stat 'ex_mem_curr->status' # Instruction status +wordsig M_icode 'ex_mem_curr->icode' # Instruction code +wordsig M_ifun 'ex_mem_curr->ifun' # Instruction function +wordsig M_valA 'ex_mem_curr->vala' # Source A value +wordsig M_dstE 'ex_mem_curr->deste' # Destination E register ID +wordsig M_valE 'ex_mem_curr->vale' # ALU E value +wordsig M_dstM 'ex_mem_curr->destm' # Destination M register ID +boolsig M_Cnd 'ex_mem_curr->takebranch' # Condition flag +boolsig dmem_error 'dmem_error' # Error signal from instruction memory + +##### Intermediate Values in Memory Stage ########################## +wordsig m_valM 'mem_wb_next->valm' # valM generated by memory +wordsig m_stat 'mem_wb_next->status' # stat (possibly modified to be SADR) + +##### Pipeline Register W ########################################## +wordsig W_stat 'mem_wb_curr->status' # Instruction status +wordsig W_icode 'mem_wb_curr->icode' # Instruction code +wordsig W_dstE 'mem_wb_curr->deste' # Destination E register ID +wordsig W_valE 'mem_wb_curr->vale' # ALU E value +wordsig W_dstM 'mem_wb_curr->destm' # Destination M register ID +wordsig W_valM 'mem_wb_curr->valm' # Memory M value + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +## What address should instruction be fetched at +word f_pc = [ + # Completion of RET instruction + W_icode == IRET : W_valM; + # Default: Use predicted value of PC + M_icode != IJXX || M_ifun == UNCOND : F_predPC; + # Mispredicted branch. Fetch at incremented PC + M_valA < M_valE && M_Cnd : M_valE; + M_valA > M_valE && !M_Cnd : M_valA; + 1 : F_predPC; +]; + +## Determine icode of fetched instruction +word f_icode = [ + imem_error : INOP; + 1: imem_icode; +]; + +# Determine ifun +word f_ifun = [ + imem_error : FNONE; + 1: imem_ifun; +]; + +# Is instruction valid? +bool instr_valid = f_icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ }; + +# Determine status code for fetched instruction +word f_stat = [ + imem_error: SADR; + !instr_valid : SINS; + f_icode == IHALT : SHLT; + 1 : SAOK; +]; + +# Does fetched instruction require a regid byte? +bool need_regids = + f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +# Predict next value of PC +word f_predPC = [ + # BBTFNT: This is where you'll change the branch prediction rule + f_icode == ICALL || + f_icode == IJXX && (f_ifun == UNCOND || f_valC < f_valP) : f_valC; + 1 : f_valP; +]; + +################ Decode Stage ###################################### + + +## What register should be used as the A source? +word d_srcA = [ + D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA; + D_icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word d_srcB = [ + D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word d_dstE = [ + D_icode in { IRRMOVQ, IIRMOVQ, IOPQ} : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word d_dstM = [ + D_icode in { IMRMOVQ, IPOPQ } : D_rA; + 1 : RNONE; # Don't write any register +]; + +## What should be the A value? +## Forward into decode stage for valA +word d_valA = [ + D_icode in { ICALL, IJXX } : D_valP; # Use incremented PC + d_srcA == e_dstE : e_valE; # Forward valE from execute + d_srcA == M_dstM : m_valM; # Forward valM from memory + d_srcA == M_dstE : M_valE; # Forward valE from memory + d_srcA == W_dstM : W_valM; # Forward valM from write back + d_srcA == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalA; # Use value read from register file +]; + +word d_valB = [ + d_srcB == e_dstE : e_valE; # Forward valE from execute + d_srcB == M_dstM : m_valM; # Forward valM from memory + d_srcB == M_dstE : M_valE; # Forward valE from memory + d_srcB == W_dstM : W_valM; # Forward valM from write back + d_srcB == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalB; # Use value read from register file +]; + +################ Execute Stage ##################################### + +# BBTFNT: When some branches are predicted as not-taken, you need some +# way to get valC into pipeline register M, so that +# you can correct for a mispredicted branch. + +## Select input A to ALU +word aluA = [ + E_icode in { IRRMOVQ, IOPQ } : E_valA; + E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX } : E_valC; + E_icode in { ICALL, IPUSHQ } : -8; + E_icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ } : E_valB; + E_icode in { IRRMOVQ, IIRMOVQ, IJXX } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + E_icode == IOPQ : E_ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = E_icode == IOPQ && + # State changes only during normal operation + !m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT }; + +## Generate valA in execute stage +word e_valA = E_valA; # Pass valA through stage + +## Set dstE to RNONE in event of not-taken conditional move +word e_dstE = [ + E_icode == IRRMOVQ && !e_Cnd : RNONE; + 1 : E_dstE; +]; + +################ Memory Stage ###################################### + +## Select memory address +word mem_addr = [ + M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : M_valE; + M_icode in { IPOPQ, IRET } : M_valA; + # Other instructions don't need address +]; + +## Set read control signal +bool mem_read = M_icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL }; + +#/* $begin pipe-m_stat-hcl */ +## Update the status +word m_stat = [ + dmem_error : SADR; + 1 : M_stat; +]; +#/* $end pipe-m_stat-hcl */ + +## Set E port register ID +word w_dstE = W_dstE; + +## Set E port value +word w_valE = W_valE; + +## Set M port register ID +word w_dstM = W_dstM; + +## Set M port value +word w_valM = W_valM; + +## Update processor status +word Stat = [ + W_stat == SBUB : SAOK; + 1 : W_stat; +]; + +################ Pipeline Register Control ######################### + +# Should I stall or inject a bubble into Pipeline Register F? +# At most one of these can be true. +bool F_bubble = 0; +bool F_stall = + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } || + # Stalling at fetch while ret passes through pipeline + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register D? +# At most one of these can be true. +bool D_stall = + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB }; + +bool D_bubble = + # Mispredicted branch + (E_icode == IJXX && E_ifun != UNCOND && + (E_valA < e_valE && e_Cnd || + E_valA > e_valE && !e_Cnd)) || + # BBTFNT: This condition will change + # Stalling at fetch while ret passes through pipeline + # but not condition for a load/use hazard + !(E_icode in { IMRMOVQ, IPOPQ } && E_dstM in { d_srcA, d_srcB }) && + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register E? +# At most one of these can be true. +bool E_stall = 0; +bool E_bubble = + # Mispredicted branch + (E_icode == IJXX && E_ifun != UNCOND && + (E_valA < e_valE && e_Cnd || + E_valA > e_valE && !e_Cnd)) || + # BBTFNT: This condition will change + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB}; + +# Should I stall or inject a bubble into Pipeline Register M? +# At most one of these can be true. +bool M_stall = 0; +# Start injecting bubbles as soon as exception passes through memory stage +bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT }; + +# Should I stall or inject a bubble into Pipeline Register W? +bool W_stall = W_stat in { SADR, SINS, SHLT }; +bool W_bubble = 0; +#/* $end pipe-all-hcl */ diff --git a/arch/sim/pipe/pipe-full.hcl b/arch/sim/pipe/pipe-full.hcl new file mode 100644 index 0000000..1d666c5 --- /dev/null +++ b/arch/sim/pipe/pipe-full.hcl @@ -0,0 +1,374 @@ +#/* $begin pipe-all-hcl */ +#################################################################### +# HCL Description of Control for Pipelined Y86-64 Processor # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2014 # +#################################################################### + +## Your task is to implement the iaddq instruction +## The file contains a declaration of the icodes +## for iaddq (IIADDQ) +## Your job is to add the rest of the logic to make it work + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "pipeline.h"' +quote '#include "stages.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'int main(int argc, char *argv[]){return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' +# Instruction code for iaddq instruction +wordsig IIADDQ 'I_IADDQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ########################## +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SBUB 'STAT_BUB' # Bubble in stage +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic ############## + +##### Pipeline Register F ########################################## + +wordsig F_predPC 'pc_curr->pc' # Predicted value of PC + +##### Intermediate Values in Fetch Stage ########################### + +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig f_icode 'if_id_next->icode' # (Possibly modified) instruction code +wordsig f_ifun 'if_id_next->ifun' # Fetched instruction function +wordsig f_valC 'if_id_next->valc' # Constant data of fetched instruction +wordsig f_valP 'if_id_next->valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Pipeline Register D ########################################## +wordsig D_icode 'if_id_curr->icode' # Instruction code +wordsig D_rA 'if_id_curr->ra' # rA field from instruction +wordsig D_rB 'if_id_curr->rb' # rB field from instruction +wordsig D_valP 'if_id_curr->valp' # Incremented PC + +##### Intermediate Values in Decode Stage ######################### + +wordsig d_srcA 'id_ex_next->srca' # srcA from decoded instruction +wordsig d_srcB 'id_ex_next->srcb' # srcB from decoded instruction +wordsig d_rvalA 'd_regvala' # valA read from register file +wordsig d_rvalB 'd_regvalb' # valB read from register file + +##### Pipeline Register E ########################################## +wordsig E_icode 'id_ex_curr->icode' # Instruction code +wordsig E_ifun 'id_ex_curr->ifun' # Instruction function +wordsig E_valC 'id_ex_curr->valc' # Constant data +wordsig E_srcA 'id_ex_curr->srca' # Source A register ID +wordsig E_valA 'id_ex_curr->vala' # Source A value +wordsig E_srcB 'id_ex_curr->srcb' # Source B register ID +wordsig E_valB 'id_ex_curr->valb' # Source B value +wordsig E_dstE 'id_ex_curr->deste' # Destination E register ID +wordsig E_dstM 'id_ex_curr->destm' # Destination M register ID + +##### Intermediate Values in Execute Stage ######################### +wordsig e_valE 'ex_mem_next->vale' # valE generated by ALU +boolsig e_Cnd 'ex_mem_next->takebranch' # Does condition hold? +wordsig e_dstE 'ex_mem_next->deste' # dstE (possibly modified to be RNONE) + +##### Pipeline Register M ######################### +wordsig M_stat 'ex_mem_curr->status' # Instruction status +wordsig M_icode 'ex_mem_curr->icode' # Instruction code +wordsig M_ifun 'ex_mem_curr->ifun' # Instruction function +wordsig M_valA 'ex_mem_curr->vala' # Source A value +wordsig M_dstE 'ex_mem_curr->deste' # Destination E register ID +wordsig M_valE 'ex_mem_curr->vale' # ALU E value +wordsig M_dstM 'ex_mem_curr->destm' # Destination M register ID +boolsig M_Cnd 'ex_mem_curr->takebranch' # Condition flag +boolsig dmem_error 'dmem_error' # Error signal from instruction memory + +##### Intermediate Values in Memory Stage ########################## +wordsig m_valM 'mem_wb_next->valm' # valM generated by memory +wordsig m_stat 'mem_wb_next->status' # stat (possibly modified to be SADR) + +##### Pipeline Register W ########################################## +wordsig W_stat 'mem_wb_curr->status' # Instruction status +wordsig W_icode 'mem_wb_curr->icode' # Instruction code +wordsig W_dstE 'mem_wb_curr->deste' # Destination E register ID +wordsig W_valE 'mem_wb_curr->vale' # ALU E value +wordsig W_dstM 'mem_wb_curr->destm' # Destination M register ID +wordsig W_valM 'mem_wb_curr->valm' # Memory M value + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +## What address should instruction be fetched at +word f_pc = [ + # Mispredicted branch. Fetch at incremented PC + M_icode == IJXX && !M_Cnd : M_valA; + # Completion of RET instruction + W_icode == IRET : W_valM; + # Default: Use predicted value of PC + 1 : F_predPC; +]; + +## Determine icode of fetched instruction +word f_icode = [ + imem_error : INOP; + 1: imem_icode; +]; + +# Determine ifun +word f_ifun = [ + imem_error : FNONE; + 1: imem_ifun; +]; + +# Is instruction valid? +bool instr_valid = f_icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ, IIADDQ }; + +# Determine status code for fetched instruction +word f_stat = [ + imem_error: SADR; + !instr_valid : SINS; + f_icode == IHALT : SHLT; + 1 : SAOK; +]; + +# Does fetched instruction require a regid byte? +bool need_regids = + f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL, IIADDQ }; + +# Predict next value of PC +word f_predPC = [ + f_icode in { IJXX, ICALL } : f_valC; + 1 : f_valP; +]; + +################ Decode Stage ###################################### + + +## What register should be used as the A source? +word d_srcA = [ + D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA; + D_icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word d_srcB = [ + D_icode in { IOPQ, IRMMOVQ, IMRMOVQ, IIADDQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word d_dstE = [ + D_icode in { IRRMOVQ, IIRMOVQ, IOPQ, IIADDQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word d_dstM = [ + D_icode in { IMRMOVQ, IPOPQ } : D_rA; + 1 : RNONE; # Don't write any register +]; + +## What should be the A value? +## Forward into decode stage for valA +word d_valA = [ + D_icode in { ICALL, IJXX } : D_valP; # Use incremented PC + d_srcA == e_dstE : e_valE; # Forward valE from execute + d_srcA == M_dstM : m_valM; # Forward valM from memory + d_srcA == M_dstE : M_valE; # Forward valE from memory + d_srcA == W_dstM : W_valM; # Forward valM from write back + d_srcA == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalA; # Use value read from register file +]; + +word d_valB = [ + d_srcB == e_dstE : e_valE; # Forward valE from execute + d_srcB == M_dstM : m_valM; # Forward valM from memory + d_srcB == M_dstE : M_valE; # Forward valE from memory + d_srcB == W_dstM : W_valM; # Forward valM from write back + d_srcB == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalB; # Use value read from register file +]; + +################ Execute Stage ##################################### + +## Select input A to ALU +word aluA = [ + E_icode in { IRRMOVQ, IOPQ } : E_valA; + E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ } : E_valC; + E_icode in { ICALL, IPUSHQ } : -8; + E_icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ, IIADDQ } : E_valB; + E_icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + E_icode == IOPQ : E_ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = E_icode in { IOPQ, IIADDQ } && + # State changes only during normal operation + !m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT }; + +## Generate valA in execute stage +word e_valA = [ + E_icode in { IPUSHQ, IRMMOVQ } && E_srcA == M_dstM : m_valM; + 1 : E_valA; # Use valA from stage pipe register +]; + +## Set dstE to RNONE in event of not-taken conditional move +word e_dstE = [ + E_icode == IRRMOVQ && !e_Cnd : RNONE; + 1 : E_dstE; +]; + +################ Memory Stage ###################################### + +## Select memory address +word mem_addr = [ + M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : M_valE; + M_icode in { IPOPQ, IRET } : M_valA; + # Other instructions don't need address +]; + +## Set read control signal +bool mem_read = M_icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL }; + +#/* $begin pipe-m_stat-hcl */ +## Update the status +word m_stat = [ + dmem_error : SADR; + 1 : M_stat; +]; +#/* $end pipe-m_stat-hcl */ + +## Set E port register ID +word w_dstE = W_dstE; + +## Set E port value +word w_valE = W_valE; + +## Set M port register ID +word w_dstM = W_dstM; + +## Set M port value +word w_valM = W_valM; + +## Update processor status +word Stat = [ + W_stat == SBUB : SAOK; + 1 : W_stat; +]; + +################ Pipeline Register Control ######################### + +# Should I stall or inject a bubble into Pipeline Register F? +# At most one of these can be true. +bool F_bubble = 0; +bool F_stall = + # Conditions for a load/use hazard + ## Set this to the new load/use condition + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } && + !(D_icode in { IPUSHQ, IRMMOVQ } && E_dstM == d_srcA) || + # Stalling at fetch while ret passes through pipeline + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register D? +# At most one of these can be true. +bool D_stall = + # Conditions for a load/use hazard + ## Set this to the new load/use condition + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } && + !(D_icode in { IPUSHQ, IRMMOVQ } && E_dstM == d_srcA); + +bool D_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Stalling at fetch while ret passes through pipeline + # but not condition for a load/use hazard + !(E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } && + !(D_icode in { IPUSHQ, IRMMOVQ } && E_dstM == d_srcA)) && + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register E? +# At most one of these can be true. +bool E_stall = 0; +bool E_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Conditions for a load/use hazard + ## Set this to the new load/use condition + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } && + !(D_icode in { IPUSHQ, IRMMOVQ } && E_dstM == d_srcA); + +# Should I stall or inject a bubble into Pipeline Register M? +# At most one of these can be true. +bool M_stall = 0; +# Start injecting bubbles as soon as exception passes through memory stage +bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT }; + +# Should I stall or inject a bubble into Pipeline Register W? +bool W_stall = W_stat in { SADR, SINS, SHLT }; +bool W_bubble = 0; +#/* $end pipe-all-hcl */ diff --git a/arch/sim/pipe/pipe-lf.hcl b/arch/sim/pipe/pipe-lf.hcl new file mode 100644 index 0000000..ff35563 --- /dev/null +++ b/arch/sim/pipe/pipe-lf.hcl @@ -0,0 +1,379 @@ +#/* $begin pipe-all-hcl */ +#################################################################### +# HCL Description of Control for Pipelined Y86-64 Processor # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2014 # +#################################################################### + +## Your task is to implement load-forwarding, where a value +## read from memory can be stored to memory by the immediately +## following instruction without stalling +## This requires modifying the definition of e_valA +## and relaxing the stall conditions. Relevant sections to change +## are shown in comments containing the keyword "LB" + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "pipeline.h"' +quote '#include "stages.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'int main(int argc, char *argv[]){return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ########################## +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SBUB 'STAT_BUB' # Bubble in stage +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic ############## + +##### Pipeline Register F ########################################## + +wordsig F_predPC 'pc_curr->pc' # Predicted value of PC + +##### Intermediate Values in Fetch Stage ########################### + +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig f_icode 'if_id_next->icode' # (Possibly modified) instruction code +wordsig f_ifun 'if_id_next->ifun' # Fetched instruction function +wordsig f_valC 'if_id_next->valc' # Constant data of fetched instruction +wordsig f_valP 'if_id_next->valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Pipeline Register D ########################################## +wordsig D_icode 'if_id_curr->icode' # Instruction code +wordsig D_rA 'if_id_curr->ra' # rA field from instruction +wordsig D_rB 'if_id_curr->rb' # rB field from instruction +wordsig D_valP 'if_id_curr->valp' # Incremented PC + +##### Intermediate Values in Decode Stage ######################### + +wordsig d_srcA 'id_ex_next->srca' # srcA from decoded instruction +wordsig d_srcB 'id_ex_next->srcb' # srcB from decoded instruction +wordsig d_rvalA 'd_regvala' # valA read from register file +wordsig d_rvalB 'd_regvalb' # valB read from register file + +##### Pipeline Register E ########################################## +wordsig E_icode 'id_ex_curr->icode' # Instruction code +wordsig E_ifun 'id_ex_curr->ifun' # Instruction function +wordsig E_valC 'id_ex_curr->valc' # Constant data +wordsig E_srcA 'id_ex_curr->srca' # Source A register ID +wordsig E_valA 'id_ex_curr->vala' # Source A value +wordsig E_srcB 'id_ex_curr->srcb' # Source B register ID +wordsig E_valB 'id_ex_curr->valb' # Source B value +wordsig E_dstE 'id_ex_curr->deste' # Destination E register ID +wordsig E_dstM 'id_ex_curr->destm' # Destination M register ID + +##### Intermediate Values in Execute Stage ######################### +wordsig e_valE 'ex_mem_next->vale' # valE generated by ALU +boolsig e_Cnd 'ex_mem_next->takebranch' # Does condition hold? +wordsig e_dstE 'ex_mem_next->deste' # dstE (possibly modified to be RNONE) + +##### Pipeline Register M ######################### +wordsig M_stat 'ex_mem_curr->status' # Instruction status +wordsig M_icode 'ex_mem_curr->icode' # Instruction code +wordsig M_ifun 'ex_mem_curr->ifun' # Instruction function +wordsig M_valA 'ex_mem_curr->vala' # Source A value +wordsig M_dstE 'ex_mem_curr->deste' # Destination E register ID +wordsig M_valE 'ex_mem_curr->vale' # ALU E value +wordsig M_dstM 'ex_mem_curr->destm' # Destination M register ID +boolsig M_Cnd 'ex_mem_curr->takebranch' # Condition flag +boolsig dmem_error 'dmem_error' # Error signal from instruction memory +## LF: Carry srcA up to pipeline register M +wordsig M_srcA 'ex_mem_curr->srca' # Source A register ID + +##### Intermediate Values in Memory Stage ########################## +wordsig m_valM 'mem_wb_next->valm' # valM generated by memory +wordsig m_stat 'mem_wb_next->status' # stat (possibly modified to be SADR) + +##### Pipeline Register W ########################################## +wordsig W_stat 'mem_wb_curr->status' # Instruction status +wordsig W_icode 'mem_wb_curr->icode' # Instruction code +wordsig W_dstE 'mem_wb_curr->deste' # Destination E register ID +wordsig W_valE 'mem_wb_curr->vale' # ALU E value +wordsig W_dstM 'mem_wb_curr->destm' # Destination M register ID +wordsig W_valM 'mem_wb_curr->valm' # Memory M value + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +## What address should instruction be fetched at +word f_pc = [ + # Mispredicted branch. Fetch at incremented PC + M_icode == IJXX && !M_Cnd : M_valA; + # Completion of RET instruction + W_icode == IRET : W_valM; + # Default: Use predicted value of PC + 1 : F_predPC; +]; + +## Determine icode of fetched instruction +word f_icode = [ + imem_error : INOP; + 1: imem_icode; +]; + +# Determine ifun +word f_ifun = [ + imem_error : FNONE; + 1: imem_ifun; +]; + +# Is instruction valid? +bool instr_valid = f_icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ }; + +# Determine status code for fetched instruction +word f_stat = [ + imem_error: SADR; + !instr_valid : SINS; + f_icode == IHALT : SHLT; + 1 : SAOK; +]; + +# Does fetched instruction require a regid byte? +bool need_regids = + f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +# Predict next value of PC +word f_predPC = [ + f_icode in { IJXX, ICALL } : f_valC; + 1 : f_valP; +]; + +################ Decode Stage ###################################### + + +## What register should be used as the A source? +word d_srcA = [ + D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA; + D_icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word d_srcB = [ + D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word d_dstE = [ + D_icode in { IRRMOVQ, IIRMOVQ, IOPQ} : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word d_dstM = [ + D_icode in { IMRMOVQ, IPOPQ } : D_rA; + 1 : RNONE; # Don't write any register +]; + +## What should be the A value? +## Forward into decode stage for valA +word d_valA = [ + D_icode in { ICALL, IJXX } : D_valP; # Use incremented PC + d_srcA == e_dstE : e_valE; # Forward valE from execute + d_srcA == M_dstM : m_valM; # Forward valM from memory + d_srcA == M_dstE : M_valE; # Forward valE from memory + d_srcA == W_dstM : W_valM; # Forward valM from write back + d_srcA == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalA; # Use value read from register file +]; + +word d_valB = [ + d_srcB == e_dstE : e_valE; # Forward valE from execute + d_srcB == M_dstM : m_valM; # Forward valM from memory + d_srcB == M_dstE : M_valE; # Forward valE from memory + d_srcB == W_dstM : W_valM; # Forward valM from write back + d_srcB == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalB; # Use value read from register file +]; + +################ Execute Stage ##################################### + +## Select input A to ALU +word aluA = [ + E_icode in { IRRMOVQ, IOPQ } : E_valA; + E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : E_valC; + E_icode in { ICALL, IPUSHQ } : -8; + E_icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ } : E_valB; + E_icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + E_icode == IOPQ : E_ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = E_icode == IOPQ && + # State changes only during normal operation + !m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT }; + +## Generate valA in execute stage +## LB: With load forwarding, want to insert valM +## from memory stage when appropriate +## Here it is set to the default used in the normal pipeline +word e_valA = [ + E_icode in { IPUSHQ, IRMMOVQ } && E_srcA == M_dstM : m_valM; + 1 : E_valA; # Use valA from stage pipe register +]; + +## Set dstE to RNONE in event of not-taken conditional move +word e_dstE = [ + E_icode == IRRMOVQ && !e_Cnd : RNONE; + 1 : E_dstE; +]; + +################ Memory Stage ###################################### + +## Select memory address +word mem_addr = [ + M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : M_valE; + M_icode in { IPOPQ, IRET } : M_valA; + # Other instructions don't need address +]; + +## Set read control signal +bool mem_read = M_icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL }; + +#/* $begin pipe-m_stat-hcl */ +## Update the status +word m_stat = [ + dmem_error : SADR; + 1 : M_stat; +]; +#/* $end pipe-m_stat-hcl */ + +## Set E port register ID +word w_dstE = W_dstE; + +## Set E port value +word w_valE = W_valE; + +## Set M port register ID +word w_dstM = W_dstM; + +## Set M port value +word w_valM = W_valM; + +## Update processor status +word Stat = [ + W_stat == SBUB : SAOK; + 1 : W_stat; +]; + +################ Pipeline Register Control ######################### + +# Should I stall or inject a bubble into Pipeline Register F? +# At most one of these can be true. +bool F_bubble = 0; +bool F_stall = + # Conditions for a load/use hazard + ## Set this to the new load/use condition + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } && + !(D_icode in { IPUSHQ, IRMMOVQ } && E_dstM == d_srcA) || + # Stalling at fetch while ret passes through pipeline + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register D? +# At most one of these can be true. +bool D_stall = + # Conditions for a load/use hazard + ## Set this to the new load/use condition + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } && + !(D_icode in { IPUSHQ, IRMMOVQ } && E_dstM == d_srcA); + +bool D_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Stalling at fetch while ret passes through pipeline + # but not condition for a load/use hazard + !(E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } && + !(D_icode in { IPUSHQ, IRMMOVQ } && E_dstM == d_srcA)) && + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register E? +# At most one of these can be true. +bool E_stall = 0; +bool E_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Conditions for a load/use hazard + ## Set this to the new load/use condition + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } && + !(D_icode in { IPUSHQ, IRMMOVQ } && E_dstM == d_srcA); + +# Should I stall or inject a bubble into Pipeline Register M? +# At most one of these can be true. +bool M_stall = 0; +# Start injecting bubbles as soon as exception passes through memory stage +bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT }; + +# Should I stall or inject a bubble into Pipeline Register W? +bool W_stall = W_stat in { SADR, SINS, SHLT }; +bool W_bubble = 0; +#/* $end pipe-all-hcl */ diff --git a/arch/sim/pipe/pipe-nobypass.hcl b/arch/sim/pipe/pipe-nobypass.hcl new file mode 100644 index 0000000..68a0352 --- /dev/null +++ b/arch/sim/pipe/pipe-nobypass.hcl @@ -0,0 +1,353 @@ +#/* $begin pipe-all-hcl */ +#################################################################### +# HCL Description of Control for Pipelined Y86-64 Processor # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2014 # +#################################################################### + +## Your task is to make the pipeline work without using any forwarding +## The normal bypassing logic in the file is disabled. +## You can only change the pipeline control logic at the end of this file. +## The trick is to make the pipeline stall whenever there is a data hazard. + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "pipeline.h"' +quote '#include "stages.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'int main(int argc, char *argv[]){return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ########################## +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SBUB 'STAT_BUB' # Bubble in stage +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic ############## + +##### Pipeline Register F ########################################## + +wordsig F_predPC 'pc_curr->pc' # Predicted value of PC + +##### Intermediate Values in Fetch Stage ########################### + +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig f_icode 'if_id_next->icode' # (Possibly modified) instruction code +wordsig f_ifun 'if_id_next->ifun' # Fetched instruction function +wordsig f_valC 'if_id_next->valc' # Constant data of fetched instruction +wordsig f_valP 'if_id_next->valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Pipeline Register D ########################################## +wordsig D_icode 'if_id_curr->icode' # Instruction code +wordsig D_rA 'if_id_curr->ra' # rA field from instruction +wordsig D_rB 'if_id_curr->rb' # rB field from instruction +wordsig D_valP 'if_id_curr->valp' # Incremented PC + +##### Intermediate Values in Decode Stage ######################### + +wordsig d_srcA 'id_ex_next->srca' # srcA from decoded instruction +wordsig d_srcB 'id_ex_next->srcb' # srcB from decoded instruction +wordsig d_rvalA 'd_regvala' # valA read from register file +wordsig d_rvalB 'd_regvalb' # valB read from register file + +##### Pipeline Register E ########################################## +wordsig E_icode 'id_ex_curr->icode' # Instruction code +wordsig E_ifun 'id_ex_curr->ifun' # Instruction function +wordsig E_valC 'id_ex_curr->valc' # Constant data +wordsig E_srcA 'id_ex_curr->srca' # Source A register ID +wordsig E_valA 'id_ex_curr->vala' # Source A value +wordsig E_srcB 'id_ex_curr->srcb' # Source B register ID +wordsig E_valB 'id_ex_curr->valb' # Source B value +wordsig E_dstE 'id_ex_curr->deste' # Destination E register ID +wordsig E_dstM 'id_ex_curr->destm' # Destination M register ID + +##### Intermediate Values in Execute Stage ######################### +wordsig e_valE 'ex_mem_next->vale' # valE generated by ALU +boolsig e_Cnd 'ex_mem_next->takebranch' # Does condition hold? +wordsig e_dstE 'ex_mem_next->deste' # dstE (possibly modified to be RNONE) + +##### Pipeline Register M ######################### +wordsig M_stat 'ex_mem_curr->status' # Instruction status +wordsig M_icode 'ex_mem_curr->icode' # Instruction code +wordsig M_ifun 'ex_mem_curr->ifun' # Instruction function +wordsig M_valA 'ex_mem_curr->vala' # Source A value +wordsig M_dstE 'ex_mem_curr->deste' # Destination E register ID +wordsig M_valE 'ex_mem_curr->vale' # ALU E value +wordsig M_dstM 'ex_mem_curr->destm' # Destination M register ID +boolsig M_Cnd 'ex_mem_curr->takebranch' # Condition flag +boolsig dmem_error 'dmem_error' # Error signal from instruction memory + +##### Intermediate Values in Memory Stage ########################## +wordsig m_valM 'mem_wb_next->valm' # valM generated by memory +wordsig m_stat 'mem_wb_next->status' # stat (possibly modified to be SADR) + +##### Pipeline Register W ########################################## +wordsig W_stat 'mem_wb_curr->status' # Instruction status +wordsig W_icode 'mem_wb_curr->icode' # Instruction code +wordsig W_dstE 'mem_wb_curr->deste' # Destination E register ID +wordsig W_valE 'mem_wb_curr->vale' # ALU E value +wordsig W_dstM 'mem_wb_curr->destm' # Destination M register ID +wordsig W_valM 'mem_wb_curr->valm' # Memory M value + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +## What address should instruction be fetched at +word f_pc = [ + # Mispredicted branch. Fetch at incremented PC + M_icode == IJXX && !M_Cnd : M_valA; + # Completion of RET instruction + W_icode == IRET : W_valM; + # Default: Use predicted value of PC + 1 : F_predPC; +]; + +## Determine icode of fetched instruction +word f_icode = [ + imem_error : INOP; + 1: imem_icode; +]; + +# Determine ifun +word f_ifun = [ + imem_error : FNONE; + 1: imem_ifun; +]; + +# Is instruction valid? +bool instr_valid = f_icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ }; + +# Determine status code for fetched instruction +word f_stat = [ + imem_error: SADR; + !instr_valid : SINS; + f_icode == IHALT : SHLT; + 1 : SAOK; +]; + +# Does fetched instruction require a regid byte? +bool need_regids = + f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +# Predict next value of PC +word f_predPC = [ + f_icode in { IJXX, ICALL } : f_valC; + 1 : f_valP; +]; + +################ Decode Stage ###################################### + + +## What register should be used as the A source? +word d_srcA = [ + D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA; + D_icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word d_srcB = [ + D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word d_dstE = [ + D_icode in { IRRMOVQ, IIRMOVQ, IOPQ} : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word d_dstM = [ + D_icode in { IMRMOVQ, IPOPQ } : D_rA; + 1 : RNONE; # Don't write any register +]; + +## What should be the A value? +## DO NOT MODIFY THE FOLLOWING CODE. +## No forwarding. valA is either valP or value from register file +word d_valA = [ + D_icode in { ICALL, IJXX } : D_valP; # Use incremented PC + 1 : d_rvalA; # Use value read from register file +]; + +## No forwarding. valB is value from register file +word d_valB = d_rvalB; + +################ Execute Stage ##################################### + +## Select input A to ALU +word aluA = [ + E_icode in { IRRMOVQ, IOPQ } : E_valA; + E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : E_valC; + E_icode in { ICALL, IPUSHQ } : -8; + E_icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ } : E_valB; + E_icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + E_icode == IOPQ : E_ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = E_icode == IOPQ && + # State changes only during normal operation + !m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT }; + +## Generate valA in execute stage +word e_valA = E_valA; # Pass valA through stage + +## Set dstE to RNONE in event of not-taken conditional move +word e_dstE = [ + E_icode == IRRMOVQ && !e_Cnd : RNONE; + 1 : E_dstE; +]; + +################ Memory Stage ###################################### + +## Select memory address +word mem_addr = [ + M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : M_valE; + M_icode in { IPOPQ, IRET } : M_valA; + # Other instructions don't need address +]; + +## Set read control signal +bool mem_read = M_icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL }; + +#/* $begin pipe-m_stat-hcl */ +## Update the status +word m_stat = [ + dmem_error : SADR; + 1 : M_stat; +]; +#/* $end pipe-m_stat-hcl */ + +## Set E port register ID +word w_dstE = W_dstE; + +## Set E port value +word w_valE = W_valE; + +## Set M port register ID +word w_dstM = W_dstM; + +## Set M port value +word w_valM = W_valM; + +## Update processor status +word Stat = [ + W_stat == SBUB : SAOK; + 1 : W_stat; +]; + +################ Pipeline Register Control ######################### + +# Should I stall or inject a bubble into Pipeline Register F? +# At most one of these can be true. +bool F_bubble = 0; +bool F_stall = + # Modify the following to stall the update of pipeline register F + d_srcA != RNONE && d_srcA in { E_dstE, E_dstM, M_dstE, M_dstM, W_dstE, W_dstM } || + d_srcB != RNONE && d_srcB in { E_dstE, E_dstM, M_dstE, M_dstM, W_dstE, W_dstM } || + # Stalling at fetch while ret passes through pipeline + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register D? +# At most one of these can be true. +bool D_stall = + # Modify the following to stall the instruction in decode + (d_srcA != RNONE && d_srcA in { E_dstE, E_dstM, M_dstE, M_dstM, W_dstE, W_dstM } || + d_srcB != RNONE && d_srcB in { E_dstE, E_dstM, M_dstE, M_dstM, W_dstE, W_dstM }) && + !(E_icode == IJXX && !e_Cnd); + +bool D_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Stalling at fetch while ret passes through pipeline + IRET in { D_icode, E_icode, M_icode } && + # but not condition for a generate/use hazard + !(d_srcA != RNONE && d_srcA in { E_dstE, E_dstM, M_dstE, M_dstM, W_dstE, W_dstM } || + d_srcB != RNONE && d_srcB in { E_dstE, E_dstM, M_dstE, M_dstM, W_dstE, W_dstM }); + +# Should I stall or inject a bubble into Pipeline Register E? +# At most one of these can be true. +bool E_stall = 0; +bool E_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Modify the following to inject bubble into the execute stage + d_srcA != RNONE && d_srcA in { E_dstE, E_dstM, M_dstE, M_dstM, W_dstE, W_dstM } || + d_srcB != RNONE && d_srcB in { E_dstE, E_dstM, M_dstE, M_dstM, W_dstE, W_dstM }; + +# Should I stall or inject a bubble into Pipeline Register M? +# At most one of these can be true. +bool M_stall = 0; +# Start injecting bubbles as soon as exception passes through memory stage +bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT }; + +# Should I stall or inject a bubble into Pipeline Register W? +bool W_stall = W_stat in { SADR, SINS, SHLT }; +bool W_bubble = 0; +#/* $end pipe-all-hcl */ diff --git a/arch/sim/pipe/pipe-nt.hcl b/arch/sim/pipe/pipe-nt.hcl new file mode 100644 index 0000000..df1f0dc --- /dev/null +++ b/arch/sim/pipe/pipe-nt.hcl @@ -0,0 +1,371 @@ +#/* $begin pipe-all-hcl */ +#################################################################### +# HCL Description of Control for Pipelined Y86-64 Processor # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2014 # +#################################################################### + +## Your task is to modify the design so that conditional branches are +## predicted as being not-taken. The code here is nearly identical +## to that for the normal pipeline. +## Comments starting with keyword "BNT" have been added at places +## relevant to the exercise. + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "pipeline.h"' +quote '#include "stages.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'int main(int argc, char *argv[]){return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ########################## +wordsig ALUADD 'A_ADD' # ALU should add its arguments +## BNT: For modified branch prediction, need to distinguish +## conditional vs. unconditional branches +##### Jump conditions referenced explicitly +wordsig UNCOND 'C_YES' # Unconditional transfer + +##### Possible instruction status values ##### +wordsig SBUB 'STAT_BUB' # Bubble in stage +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic ############## + +##### Pipeline Register F ########################################## + +wordsig F_predPC 'pc_curr->pc' # Predicted value of PC + +##### Intermediate Values in Fetch Stage ########################### + +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig f_icode 'if_id_next->icode' # (Possibly modified) instruction code +wordsig f_ifun 'if_id_next->ifun' # Fetched instruction function +wordsig f_valC 'if_id_next->valc' # Constant data of fetched instruction +wordsig f_valP 'if_id_next->valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Pipeline Register D ########################################## +wordsig D_icode 'if_id_curr->icode' # Instruction code +wordsig D_rA 'if_id_curr->ra' # rA field from instruction +wordsig D_rB 'if_id_curr->rb' # rB field from instruction +wordsig D_valP 'if_id_curr->valp' # Incremented PC + +##### Intermediate Values in Decode Stage ######################### + +wordsig d_srcA 'id_ex_next->srca' # srcA from decoded instruction +wordsig d_srcB 'id_ex_next->srcb' # srcB from decoded instruction +wordsig d_rvalA 'd_regvala' # valA read from register file +wordsig d_rvalB 'd_regvalb' # valB read from register file + +##### Pipeline Register E ########################################## +wordsig E_icode 'id_ex_curr->icode' # Instruction code +wordsig E_ifun 'id_ex_curr->ifun' # Instruction function +wordsig E_valC 'id_ex_curr->valc' # Constant data +wordsig E_srcA 'id_ex_curr->srca' # Source A register ID +wordsig E_valA 'id_ex_curr->vala' # Source A value +wordsig E_srcB 'id_ex_curr->srcb' # Source B register ID +wordsig E_valB 'id_ex_curr->valb' # Source B value +wordsig E_dstE 'id_ex_curr->deste' # Destination E register ID +wordsig E_dstM 'id_ex_curr->destm' # Destination M register ID + +##### Intermediate Values in Execute Stage ######################### +wordsig e_valE 'ex_mem_next->vale' # valE generated by ALU +boolsig e_Cnd 'ex_mem_next->takebranch' # Does condition hold? +wordsig e_dstE 'ex_mem_next->deste' # dstE (possibly modified to be RNONE) + +##### Pipeline Register M ######################### +wordsig M_stat 'ex_mem_curr->status' # Instruction status +wordsig M_icode 'ex_mem_curr->icode' # Instruction code +wordsig M_ifun 'ex_mem_curr->ifun' # Instruction function +wordsig M_valA 'ex_mem_curr->vala' # Source A value +wordsig M_dstE 'ex_mem_curr->deste' # Destination E register ID +wordsig M_valE 'ex_mem_curr->vale' # ALU E value +wordsig M_dstM 'ex_mem_curr->destm' # Destination M register ID +boolsig M_Cnd 'ex_mem_curr->takebranch' # Condition flag +boolsig dmem_error 'dmem_error' # Error signal from instruction memory + +##### Intermediate Values in Memory Stage ########################## +wordsig m_valM 'mem_wb_next->valm' # valM generated by memory +wordsig m_stat 'mem_wb_next->status' # stat (possibly modified to be SADR) + +##### Pipeline Register W ########################################## +wordsig W_stat 'mem_wb_curr->status' # Instruction status +wordsig W_icode 'mem_wb_curr->icode' # Instruction code +wordsig W_dstE 'mem_wb_curr->deste' # Destination E register ID +wordsig W_valE 'mem_wb_curr->vale' # ALU E value +wordsig W_dstM 'mem_wb_curr->destm' # Destination M register ID +wordsig W_valM 'mem_wb_curr->valm' # Memory M value + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +## What address should instruction be fetched at +word f_pc = [ + # Mispredicted branch. Fetch at incremented PC + M_icode == IJXX && M_ifun != UNCOND && M_Cnd : M_valE; + # Completion of RET instruction + W_icode == IRET : W_valM; + # Default: Use predicted value of PC + 1 : F_predPC; +]; + +## Determine icode of fetched instruction +word f_icode = [ + imem_error : INOP; + 1: imem_icode; +]; + +# Determine ifun +word f_ifun = [ + imem_error : FNONE; + 1: imem_ifun; +]; + +# Is instruction valid? +bool instr_valid = f_icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ }; + +# Determine status code for fetched instruction +word f_stat = [ + imem_error: SADR; + !instr_valid : SINS; + f_icode == IHALT : SHLT; + 1 : SAOK; +]; + +# Does fetched instruction require a regid byte? +bool need_regids = + f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +# Predict next value of PC +word f_predPC = [ + # BNT: This is where you'll change the branch prediction rule + f_icode == ICALL || f_icode == IJXX && f_ifun == UNCOND : f_valC; + 1 : f_valP; +]; + +################ Decode Stage ###################################### + + +## What register should be used as the A source? +word d_srcA = [ + D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA; + D_icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word d_srcB = [ + D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word d_dstE = [ + D_icode in { IRRMOVQ, IIRMOVQ, IOPQ} : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word d_dstM = [ + D_icode in { IMRMOVQ, IPOPQ } : D_rA; + 1 : RNONE; # Don't write any register +]; + +## What should be the A value? +## Forward into decode stage for valA +word d_valA = [ + D_icode == ICALL : D_valP; # Use incremented PC + d_srcA == e_dstE : e_valE; # Forward valE from execute + d_srcA == M_dstM : m_valM; # Forward valM from memory + d_srcA == M_dstE : M_valE; # Forward valE from memory + d_srcA == W_dstM : W_valM; # Forward valM from write back + d_srcA == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalA; # Use value read from register file +]; + +word d_valB = [ + d_srcB == e_dstE : e_valE; # Forward valE from execute + d_srcB == M_dstM : m_valM; # Forward valM from memory + d_srcB == M_dstE : M_valE; # Forward valE from memory + d_srcB == W_dstM : W_valM; # Forward valM from write back + d_srcB == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalB; # Use value read from register file +]; + +################ Execute Stage ##################################### + +# BNT: When some branches are predicted as not-taken, you need some +# way to get valC into pipeline register M, so that +# you can correct for a mispredicted branch. + +## Select input A to ALU +word aluA = [ + E_icode in { IRRMOVQ, IOPQ } : E_valA; + E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX } : E_valC; + E_icode in { ICALL, IPUSHQ } : -8; + E_icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ } : E_valB; + E_icode in { IRRMOVQ, IIRMOVQ, IJXX } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + E_icode == IOPQ : E_ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = E_icode == IOPQ && + # State changes only during normal operation + !m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT }; + +## Generate valA in execute stage +word e_valA = E_valA; # Pass valA through stage + +## Set dstE to RNONE in event of not-taken conditional move +word e_dstE = [ + E_icode == IRRMOVQ && !e_Cnd : RNONE; + 1 : E_dstE; +]; + +################ Memory Stage ###################################### + +## Select memory address +word mem_addr = [ + M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : M_valE; + M_icode in { IPOPQ, IRET } : M_valA; + # Other instructions don't need address +]; + +## Set read control signal +bool mem_read = M_icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL }; + +#/* $begin pipe-m_stat-hcl */ +## Update the status +word m_stat = [ + dmem_error : SADR; + 1 : M_stat; +]; +#/* $end pipe-m_stat-hcl */ + +## Set E port register ID +word w_dstE = W_dstE; + +## Set E port value +word w_valE = W_valE; + +## Set M port register ID +word w_dstM = W_dstM; + +## Set M port value +word w_valM = W_valM; + +## Update processor status +word Stat = [ + W_stat == SBUB : SAOK; + 1 : W_stat; +]; + +################ Pipeline Register Control ######################### + +# Should I stall or inject a bubble into Pipeline Register F? +# At most one of these can be true. +bool F_bubble = 0; +bool F_stall = + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } || + # Stalling at fetch while ret passes through pipeline + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register D? +# At most one of these can be true. +bool D_stall = + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB }; + +bool D_bubble = + # Mispredicted branch + (E_icode == IJXX && E_ifun != UNCOND && e_Cnd) || + # Stalling at fetch while ret passes through pipeline + # but not condition for a load/use hazard + !(E_icode in { IMRMOVQ, IPOPQ } && E_dstM in { d_srcA, d_srcB }) && + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register E? +# At most one of these can be true. +bool E_stall = 0; +bool E_bubble = + # Mispredicted branch + (E_icode == IJXX && E_ifun != UNCOND && e_Cnd) || + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB}; + +# Should I stall or inject a bubble into Pipeline Register M? +# At most one of these can be true. +bool M_stall = 0; +# Start injecting bubbles as soon as exception passes through memory stage +bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT }; + +# Should I stall or inject a bubble into Pipeline Register W? +bool W_stall = W_stat in { SADR, SINS, SHLT }; +bool W_bubble = 0; +#/* $end pipe-all-hcl */ diff --git a/arch/sim/pipe/pipe-std.hcl b/arch/sim/pipe/pipe-std.hcl new file mode 100644 index 0000000..1bc8ce8 --- /dev/null +++ b/arch/sim/pipe/pipe-std.hcl @@ -0,0 +1,356 @@ +#/* $begin pipe-all-hcl */ +#################################################################### +# HCL Description of Control for Pipelined Y86-64 Processor # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2014 # +#################################################################### + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "pipeline.h"' +quote '#include "stages.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'int main(int argc, char *argv[]){return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ########################## +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SBUB 'STAT_BUB' # Bubble in stage +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic ############## + +##### Pipeline Register F ########################################## + +wordsig F_predPC 'pc_curr->pc' # Predicted value of PC + +##### Intermediate Values in Fetch Stage ########################### + +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig f_icode 'if_id_next->icode' # (Possibly modified) instruction code +wordsig f_ifun 'if_id_next->ifun' # Fetched instruction function +wordsig f_valC 'if_id_next->valc' # Constant data of fetched instruction +wordsig f_valP 'if_id_next->valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Pipeline Register D ########################################## +wordsig D_icode 'if_id_curr->icode' # Instruction code +wordsig D_rA 'if_id_curr->ra' # rA field from instruction +wordsig D_rB 'if_id_curr->rb' # rB field from instruction +wordsig D_valP 'if_id_curr->valp' # Incremented PC + +##### Intermediate Values in Decode Stage ######################### + +wordsig d_srcA 'id_ex_next->srca' # srcA from decoded instruction +wordsig d_srcB 'id_ex_next->srcb' # srcB from decoded instruction +wordsig d_rvalA 'd_regvala' # valA read from register file +wordsig d_rvalB 'd_regvalb' # valB read from register file + +##### Pipeline Register E ########################################## +wordsig E_icode 'id_ex_curr->icode' # Instruction code +wordsig E_ifun 'id_ex_curr->ifun' # Instruction function +wordsig E_valC 'id_ex_curr->valc' # Constant data +wordsig E_srcA 'id_ex_curr->srca' # Source A register ID +wordsig E_valA 'id_ex_curr->vala' # Source A value +wordsig E_srcB 'id_ex_curr->srcb' # Source B register ID +wordsig E_valB 'id_ex_curr->valb' # Source B value +wordsig E_dstE 'id_ex_curr->deste' # Destination E register ID +wordsig E_dstM 'id_ex_curr->destm' # Destination M register ID + +##### Intermediate Values in Execute Stage ######################### +wordsig e_valE 'ex_mem_next->vale' # valE generated by ALU +boolsig e_Cnd 'ex_mem_next->takebranch' # Does condition hold? +wordsig e_dstE 'ex_mem_next->deste' # dstE (possibly modified to be RNONE) + +##### Pipeline Register M ######################### +wordsig M_stat 'ex_mem_curr->status' # Instruction status +wordsig M_icode 'ex_mem_curr->icode' # Instruction code +wordsig M_ifun 'ex_mem_curr->ifun' # Instruction function +wordsig M_valA 'ex_mem_curr->vala' # Source A value +wordsig M_dstE 'ex_mem_curr->deste' # Destination E register ID +wordsig M_valE 'ex_mem_curr->vale' # ALU E value +wordsig M_dstM 'ex_mem_curr->destm' # Destination M register ID +boolsig M_Cnd 'ex_mem_curr->takebranch' # Condition flag +boolsig dmem_error 'dmem_error' # Error signal from instruction memory + +##### Intermediate Values in Memory Stage ########################## +wordsig m_valM 'mem_wb_next->valm' # valM generated by memory +wordsig m_stat 'mem_wb_next->status' # stat (possibly modified to be SADR) + +##### Pipeline Register W ########################################## +wordsig W_stat 'mem_wb_curr->status' # Instruction status +wordsig W_icode 'mem_wb_curr->icode' # Instruction code +wordsig W_dstE 'mem_wb_curr->deste' # Destination E register ID +wordsig W_valE 'mem_wb_curr->vale' # ALU E value +wordsig W_dstM 'mem_wb_curr->destm' # Destination M register ID +wordsig W_valM 'mem_wb_curr->valm' # Memory M value + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +## What address should instruction be fetched at +word f_pc = [ + # Mispredicted branch. Fetch at incremented PC + M_icode == IJXX && !M_Cnd : M_valA; + # Completion of RET instruction + W_icode == IRET : W_valM; + # Default: Use predicted value of PC + 1 : F_predPC; +]; + +## Determine icode of fetched instruction +word f_icode = [ + imem_error : INOP; + 1: imem_icode; +]; + +# Determine ifun +word f_ifun = [ + imem_error : FNONE; + 1: imem_ifun; +]; + +# Is instruction valid? +bool instr_valid = f_icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ }; + +# Determine status code for fetched instruction +word f_stat = [ + imem_error: SADR; + !instr_valid : SINS; + f_icode == IHALT : SHLT; + 1 : SAOK; +]; + +# Does fetched instruction require a regid byte? +bool need_regids = + f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +# Predict next value of PC +word f_predPC = [ + f_icode in { IJXX, ICALL } : f_valC; + 1 : f_valP; +]; + +################ Decode Stage ###################################### + + +## What register should be used as the A source? +word d_srcA = [ + D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA; + D_icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word d_srcB = [ + D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word d_dstE = [ + D_icode in { IRRMOVQ, IIRMOVQ, IOPQ} : D_rB; + D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word d_dstM = [ + D_icode in { IMRMOVQ, IPOPQ } : D_rA; + 1 : RNONE; # Don't write any register +]; + +## What should be the A value? +## Forward into decode stage for valA +word d_valA = [ + D_icode in { ICALL, IJXX } : D_valP; # Use incremented PC + d_srcA == e_dstE : e_valE; # Forward valE from execute + d_srcA == M_dstM : m_valM; # Forward valM from memory + d_srcA == M_dstE : M_valE; # Forward valE from memory + d_srcA == W_dstM : W_valM; # Forward valM from write back + d_srcA == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalA; # Use value read from register file +]; + +word d_valB = [ + d_srcB == e_dstE : e_valE; # Forward valE from execute + d_srcB == M_dstM : m_valM; # Forward valM from memory + d_srcB == M_dstE : M_valE; # Forward valE from memory + d_srcB == W_dstM : W_valM; # Forward valM from write back + d_srcB == W_dstE : W_valE; # Forward valE from write back + 1 : d_rvalB; # Use value read from register file +]; + +################ Execute Stage ##################################### + +## Select input A to ALU +word aluA = [ + E_icode in { IRRMOVQ, IOPQ } : E_valA; + E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : E_valC; + E_icode in { ICALL, IPUSHQ } : -8; + E_icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ } : E_valB; + E_icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + E_icode == IOPQ : E_ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = E_icode == IOPQ && + # State changes only during normal operation + !m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT }; + +## Generate valA in execute stage +word e_valA = E_valA; # Pass valA through stage + +## Set dstE to RNONE in event of not-taken conditional move +word e_dstE = [ + E_icode == IRRMOVQ && !e_Cnd : RNONE; + 1 : E_dstE; +]; + +################ Memory Stage ###################################### + +## Select memory address +word mem_addr = [ + M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : M_valE; + M_icode in { IPOPQ, IRET } : M_valA; + # Other instructions don't need address +]; + +## Set read control signal +bool mem_read = M_icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL }; + +#/* $begin pipe-m_stat-hcl */ +## Update the status +word m_stat = [ + dmem_error : SADR; + 1 : M_stat; +]; +#/* $end pipe-m_stat-hcl */ + +## Set E port register ID +word w_dstE = W_dstE; + +## Set E port value +word w_valE = W_valE; + +## Set M port register ID +word w_dstM = W_dstM; + +## Set M port value +word w_valM = W_valM; + +## Update processor status +word Stat = [ + W_stat == SBUB : SAOK; + 1 : W_stat; +]; + +################ Pipeline Register Control ######################### + +# Should I stall or inject a bubble into Pipeline Register F? +# At most one of these can be true. +bool F_bubble = 0; +bool F_stall = + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB } || + # Stalling at fetch while ret passes through pipeline + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register D? +# At most one of these can be true. +bool D_stall = + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB }; + +bool D_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Stalling at fetch while ret passes through pipeline + # but not condition for a load/use hazard + !(E_icode in { IMRMOVQ, IPOPQ } && E_dstM in { d_srcA, d_srcB }) && + IRET in { D_icode, E_icode, M_icode }; + +# Should I stall or inject a bubble into Pipeline Register E? +# At most one of these can be true. +bool E_stall = 0; +bool E_bubble = + # Mispredicted branch + (E_icode == IJXX && !e_Cnd) || + # Conditions for a load/use hazard + E_icode in { IMRMOVQ, IPOPQ } && + E_dstM in { d_srcA, d_srcB}; + +# Should I stall or inject a bubble into Pipeline Register M? +# At most one of these can be true. +bool M_stall = 0; +# Start injecting bubbles as soon as exception passes through memory stage +bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT }; + +# Should I stall or inject a bubble into Pipeline Register W? +bool W_stall = W_stat in { SADR, SINS, SHLT }; +bool W_bubble = 0; +#/* $end pipe-all-hcl */ diff --git a/arch/sim/pipe/pipe.tcl b/arch/sim/pipe/pipe.tcl new file mode 100755 index 0000000..3129d6c --- /dev/null +++ b/arch/sim/pipe/pipe.tcl @@ -0,0 +1,633 @@ +########################################################################## +# Parsing of command line flags # +########################################################################## + +proc flagVal {flag default} { + global argv + foreach t $argv { + if {[string match "-$flag*" $t]} {return [string range $t 2 end]} + } + return $default +} + +proc findFlag {flag} { + global argv + foreach t $argv { + if {[string match "-$flag" $t]} {return 1} + } + return 0 +} + +########################################################################## +# Register File Implementation. Shown as array of 3 X 5 # +########################################################################## + + +# Font used to display register contents +set fontSize [expr 10 * [flagVal "f" 12]] +set codeFontSize [expr 10 * [flagVal "c" 10]] +set labFontSize [expr 10 * [flagVal "l" 10]] +set bigFontSize [expr 10 * [flagVal "b" 16]] +set dpyFont "*-courier-medium-r-normal--*-$fontSize-*-*-*-*-*-*" +set labFont "*-helvetica-medium-r-normal--*-$labFontSize-*-*-*-*-*-*" +set bigLabFont "*-helvetica-bold-r-normal--*-$bigFontSize-*-*-*-*-*-*" +set codeFont "*-courier-medium-r-normal--*-$codeFontSize-*-*-*-*-*-*" +# Background Color of normal register +set normalBg white +# Background Color of highlighted register +set specialBg LightSkyBlue + +# Height of titles separating major sections of control panel +set sectionHeight 2 + + +# How many rows of code do I display +set codeRowCount [flagVal "r" 50] + +# Keep track of previous highlighted register +set lastId -1 +proc setReg {id val highlight} { + global lastId normalBg specialBg + if {$lastId >= 0} { + .r.reg$lastId config -bg $normalBg + set lastId -1 + } + if {$id < 0 || $id >= 15} { + error "Invalid Register ($id)" + } + .r.reg$id config -text [format %16x $val] + if {$highlight} { + uplevel .r.reg$id config -bg $specialBg + set lastId $id + } +} + +# Clear all registers +proc clearReg {} { + global lastId normalBg + if {$lastId >= 0} { + .r.reg$lastId config -bg $normalBg + set lastId -1 + } + for {set i 0} {$i < 8} {incr i 1} { + .r.reg$i config -text "" + } +} + +# Set all 3 condition codes +proc setCC {zv cv ov} { + .cc.cc0 config -text [format %d $zv] + .cc.cc1 config -text [format %d $cv] + .cc.cc2 config -text [format %d $ov] +} + +# Set CPI display +proc showCPI {cycles instructions cpi} { + .cpi.cyc config -text [format %d $cycles] + .cpi.instr config -text [format %d $instructions] + .cpi.cpi config -text [format %.2f $cpi] +} + +# Set status display +proc showStat {s} { + .stat.val config -text $s +} + +############################################################################## +# CPI Display +############################################################################## +# Create Window for CPI display +frame .cpi +pack .cpi -in . -side bottom + +label .cpi.lab -text "Performance" -font $bigLabFont -height $sectionHeight +pack .cpi.lab -in .cpi -side left + +label .cpi.clab -text "Cycles" -font $labFont +pack .cpi.clab -in .cpi -side left +label .cpi.cyc -text "0" -width 6 -font $dpyFont -relief ridge -bg $normalBg +pack .cpi.cyc -in .cpi -side left + +label .cpi.ilab -text "Instructions" -font $labFont +pack .cpi.ilab -in .cpi -side left +label .cpi.instr -text "0" -width 6 -font $dpyFont -relief ridge -bg $normalBg +pack .cpi.instr -in .cpi -side left + +label .cpi.cpilab -text "CPI" -font $labFont +pack .cpi.cpilab -in .cpi -side left +label .cpi.cpi -text "1.0" -width 5 -font $dpyFont -relief ridge -bg $normalBg +pack .cpi.cpi -in .cpi -side left + +############################################################################## +# Status Display # +############################################################################## +# Create Window for processor status (packed next to condition codes) +frame .stat +pack .stat -in . -side bottom + +label .stat.lab -text "Stat" -width 7 -font $bigLabFont -height $sectionHeight +label .stat.val -width 3 -font $dpyFont -relief ridge -bg $normalBg +label .stat.fill -width 6 -text "" +pack .stat.lab .stat.val .stat.fill -in .stat -side left + + +############################################################################## +# Condition Code Display # +############################################################################## +# Create Window for condition codes +frame .cc +pack .cc -in .stat -side left + +label .cc.lab -text "Condition Codes" -font $bigLabFont -height $sectionHeight +pack .cc.lab -in .cc -side left + + +set ccnames [list "Z" "S" "O"] + +# Create Row of CC Labels +for {set i 0} {$i < 3} {incr i 1} { + label .cc.lab$i -width 1 -font $dpyFont -text [lindex $ccnames $i] + pack .cc.lab$i -in .cc -side left + label .cc.cc$i -width 1 -font $dpyFont -relief ridge -bg $normalBg + pack .cc.cc$i -in .cc -side left +} + +############################################################################## +# Register Display # +############################################################################## + + +# Create Window for registers +frame .r +pack .r -in . -side bottom +# Following give separate window for register file +# toplevel .r +# wm title .r "Register File" +label .r.lab -text "Register File" -font $bigLabFont -height $sectionHeight +pack .r.lab -in .r -side top +# Set up top row control panel (disabled) +# frame .r.cntl +# pack .r.cntl -fill x -in .r +# label .r.labreg -text "Register" -width 10 +# entry .r.regid -width 3 -relief sunken -textvariable regId -font $dpyFont +# label .r.labval -text "Value" -width 10 +# entry .r.regval -width 8 -relief sunken -textvariable regVal -font $dpyFont +# button .r.doset -text "Set" -command {setReg $regId $regVal 1} -width 6 +# button .r.c -text "Clear" -command clearReg -width 6 +# pack .r.labreg .r.regid .r.labval .r.regval .r.doset .r.c -in .r.cntl -side left + +set regnames [list "%rax" "%rcx" "%rdx" "%rbx" "%rsp" "%rbp" "%rsi" "%rdi" "%r8 " "%r9 " "%r10" "%r11" "%r12" "%r13" "%r14" ""] + +# Create rows of register labels and displays +for {set j 0} {$j < 3} {incr j 1} { + frame .r.labels$j + pack .r.labels$j -side top -in .r + + for {set c 0} {$c < 5} {incr c 1} { + set i [expr $j * 5 + $c] + label .r.lab$i -width 16 -font $dpyFont -text [lindex $regnames $i] + pack .r.lab$i -in .r.labels$j -side left + } + + # Create Row of Register Entries + frame .r.row$j + pack .r.row$j -side top -in .r + + # Create 5 registers + for {set c 0} {$c < 5} {incr c 1} { + set i [expr $j * 5 + $c] + if {$i == 15} { + label .r.reg$i -width 16 -font $dpyFont -text "" + } else { + label .r.reg$i -width 16 -font $dpyFont -relief ridge \ + -bg $normalBg + } + pack .r.reg$i -in .r.row$j -side left + } + +} + + +############################################################################## +# Main Control Panel # +############################################################################## +# +# Set the simulator name (defined in simname in ssim.c) +# as the title of the main window +# +wm title . $simname + +# Control Panel for simulator +set cntlBW 12 +frame .cntl +pack .cntl +button .cntl.quit -width $cntlBW -text Quit -command exit +button .cntl.run -width $cntlBW -text Go -command simGo +button .cntl.stop -width $cntlBW -text Stop -command simStop +button .cntl.step -width $cntlBW -text Step -command simStep +button .cntl.reset -width $cntlBW -text Reset -command simResetAll +pack .cntl.quit .cntl.run .cntl.stop .cntl.step .cntl.reset -in .cntl -side left +# Simulation speed control +scale .spd -label {Simulator Speed (10*log Hz)} -from -10 -to 30 -length 10c \ + -orient horizontal -command setSpeed +pack .spd + +# Simulation mode +set simMode forward + +frame .md +### Old Simulation mode stuff +#pack .md +#radiobutton .md.wedged -text Wedged -variable simMode \ +# -value wedged -width 10 -command {setSimMode wedged} +#radiobutton .md.stall -text Stall -variable simMode \ +# -value stall -width 10 -command {setSimMode stall} +#radiobutton .md.forward -text Forward -variable simMode \ +# -value forward -width 10 -command {setSimMode forward} +#pack .md.wedged .md.stall .md.forward -in .md -side left + + +# simDelay defines number of milliseconds for each cycle of simulator +# Initial value is 1000ms +set simDelay 1000 +# Set delay based on rate expressed in log(Hz) +proc setSpeed {rate} { + global simDelay + set simDelay [expr round(1000 / pow(10,$rate/10.0))] +} + +# Global variables controlling simulator execution +# Should simulator be running now? +set simGoOK 0 + +proc simStop {} { + global simGoOK + set simGoOK 0 +} + +proc simStep {} { + global simStat + set simStat [simRun 1] +} + +proc simGo {} { + global simGoOK simDelay simStat + set simGoOK 1 + # Disable the Go and Step buttons + # Enable the Stop button + while {$simGoOK} { + # run the simulator 1 cycle + after $simDelay + set simStat [simRun 1] + if {$simStat != "AOK" && $simStat != "BUB"} {set simGoOK 0} + update + } + # Disable the Stop button + # Enable the Go and Step buttons +} + +############################################################################## +# Pipe Register Display # +############################################################################## + +# Colors for Highlighting Data Sources +set valaBg LightPink +set valbBg PaleGreen1 + +# Overall width of pipe register display +set pipeWidth 72 +set pipeHeight 2 +set labWidth 5 + +# Add labeled display to window +proc addDisp {win width name} { + global dpyFont labFont + set lname [string tolower $name] + frame $win.$lname + pack $win.$lname -in $win -side left + label $win.$lname.t -text $name -font $labFont + label $win.$lname.n -width $width -font $dpyFont -bg lightgray -fg Black + label $win.$lname.c -width $width -font $dpyFont -bg white -relief ridge + pack $win.$lname.t $win.$lname.c $win.$lname.n -in $win.$lname -side top + return [list $win.$lname.n $win.$lname.c] +} + +# Set text in display row +proc setDisp {wins txts} { + for {set i 0} {$i < [llength $wins] && $i < [llength $txts]} {incr i} { + set win [lindex $wins $i] + set txt [lindex $txts $i] + $win config -text $txt + } +} + +frame .p -width $pipeWidth +pack .p -in . -side bottom +label .p.lab -text "Pipeline Registers" -font $bigLabFont -height $sectionHeight +pack .p.lab -in .p -side top +label .p.mem -text "Memory Stage" -height $pipeHeight -width $pipeWidth -bg NavyBlue -fg White -font $bigLabFont +label .p.ex -text "Execute Stage" -height $pipeHeight -width $pipeWidth -bg NavyBlue -fg White -font $bigLabFont +label .p.id -text "Decode Stage" -height $pipeHeight -width $pipeWidth -bg NavyBlue -fg White -font $bigLabFont +label .p.if -text "Fetch Stage" -height $pipeHeight -width $pipeWidth -bg NavyBlue -fg White -font $bigLabFont +frame .p.mw +frame .p.em +frame .p.de +frame .p.fd +frame .p.pc +frame .p.e +pack .p.mw .p.mem .p.em .p.ex .p.e .p.de .p.id .p.fd .p.if .p.pc -in .p -side top -anchor w -expand 1 + +proc addLabel { win nstage cstage } { + global labWidth labFont bigLabFont + frame $win.lab + label $win.name -text "$cstage" -width $labWidth -font $bigLabFont + pack $win.name -in $win.lab -side left + + label $win.lab.t -text " " -font $labFont + label $win.lab.n -width $labWidth -text "Input" -anchor w + label $win.lab.c -width $labWidth -text "State" -anchor w + pack $win.lab.t $win.lab.c $win.lab.n -in $win.lab -side top + pack $win.lab -in $win -side left +} + +addLabel .p.mw M W +addLabel .p.em E M +addLabel .p.de D E +addLabel .p.fd F D +addLabel .p.pc "" F + +proc addFill { win w } { + frame $win.fill + label $win.fill.t -text "" -width $w -bg lightgray + label $win.fill.n -bg white -text "" -width $w -bg lightgray + label $win.fill.c -bg white -text "" -width $w -bg lightgray + pack $win.fill.c $win.fill.t $win.fill.n -in $win.fill -side top -expand 1 + pack $win.fill -in $win -side right -expand 1 +} + +addFill .p.mw 0 +addFill .p.de 0 +addFill .p.fd 0 +addFill .p.pc 0 + +# Take list of lists, and transpose nesting +# Assumes all lists are of same length +proc ltranspose {inlist} { + set result {} + for {set i 0} {$i < [llength [lindex $inlist 0]]} {incr i} { + set nlist {} + for {set j 0} {$j < [llength $inlist]} {incr j} { + set ele [lindex [lindex $inlist $j] $i] + set nlist [concat $nlist [list $ele]] + } + set result [concat $result [list $nlist]] + } + return $result +} + +# Fields in F display +# Total size = 3+16 = 19 +set pwins(F) [ltranspose [list [addDisp .p.pc 3 Stat] \ + [addDisp .p.pc 16 predPC]]] + +# Fields in D display +# Total size = 3+6+4+4+16+16 = 49 +set pwins(D) [ltranspose \ + [list [addDisp .p.fd 3 Stat] \ + [addDisp .p.fd 6 Instr] \ + [addDisp .p.fd 4 rA] \ + [addDisp .p.fd 4 rB] \ + [addDisp .p.fd 16 valC] \ + [addDisp .p.fd 16 valP]]] + +# Fields in E Display +# Total size = 3+6+16+16+16+4+4+4+4 = 73 +set pwins(E) [ltranspose \ + [list [addDisp .p.de 3 Stat] \ + [addDisp .p.de 6 Instr] \ + [addDisp .p.de 16 valC] \ + [addDisp .p.de 16 valA] \ + [addDisp .p.de 16 valB] \ + [addDisp .p.de 4 dstE] \ + [addDisp .p.de 4 dstM] \ + [addDisp .p.de 4 srcA] \ + [addDisp .p.de 4 srcB]]] + +# Fields in M Display +# Total size = 3+6+3+16+16+4+4 = 52 +set pwins(M) [ltranspose \ + [list [addDisp .p.em 3 Stat] \ + [addDisp .p.em 6 Instr] \ + [addDisp .p.em 3 Cnd] \ + [addDisp .p.em 16 valE] \ + [addDisp .p.em 16 valA] \ + [addDisp .p.em 4 dstE] \ + [addDisp .p.em 4 dstM]]] +# Fields in W display +# Total size = 3+6+16+16+4+4 = 49 +set pwins(W) [ltranspose \ + [list [addDisp .p.mw 3 Stat] \ + [addDisp .p.mw 6 Instr] \ + [addDisp .p.mw 16 valE] \ + [addDisp .p.mw 16 valM] \ + [addDisp .p.mw 4 dstE] \ + [addDisp .p.mw 4 dstM]]] + +# update status line for specified pipe register +proc updateStage {name current txts} { + set Name [string toupper $name] + global pwins + set wins [lindex $pwins($Name) $current] + setDisp $wins $txts +} + +# Create Array of windows corresponding to data sources +set rwins(wm) .p.mw.valm.c +set rwins(we) .p.mw.vale.c +set rwins(me) .p.em.vale.c +set rwins(ea) .p.de.vala.c +set rwins(eb) .p.de.valb.c + +# Highlight Data Source Registers for valA, valB +proc showSources { a b } { + global rwins valaBg valbBg + # Set them all to white + foreach w [array names rwins] { + $rwins($w) config -bg White + } + if {$a != "none"} { $rwins($a) config -bg $valaBg } + if {$b != "none"} { $rwins($b) config -bg $valbBg } + + # Indicate forwarding destinations by their color + .p.de.vala.t config -bg $valaBg + .p.de.valb.t config -bg $valbBg +} + +########################################################################## +# Instruction Display # +########################################################################## + +toplevel .c +wm title .c "Program Code" +frame .c.cntl +pack .c.cntl -in .c -side top -anchor w +label .c.filelab -width 10 -text "File" +entry .c.filename -width 20 -relief sunken -textvariable codeFile \ + -font $dpyFont -bg white +button .c.loadbutton -width $cntlBW -command {loadCode $codeFile} -text Load +pack .c.filelab .c.filename .c.loadbutton -in .c.cntl -side left + +proc clearCode {} { + simLabel {} {} + destroy .c.t + destroy .c.tr +} + +proc createCode {} { + # Create Code Structure + frame .c.t + pack .c.t -in .c -side top -anchor w + # Support up to 4 columns of code, each $codeRowCount lines long + frame .c.tr + pack .c.tr -in .c.t -side top -anchor nw +} + +proc loadCode {file} { + # Kill old code window + clearCode + # Create new one + createCode + simCode $file + simResetAll +} + +# Start with initial code window, even though it will be destroyed. +createCode + +# Add a line of code to the display +proc addCodeLine {line addr op text} { + global codeRowCount + # Create new line in display + global codeFont + frame .c.tr.$addr + pack .c.tr.$addr -in .c.tr -side top -anchor w + label .c.tr.$addr.a -width 6 -text [format "0x%x" $addr] -font $codeFont + label .c.tr.$addr.i -width 20 -text $op -font $codeFont + label .c.tr.$addr.s -width 2 -text "" -font $codeFont -bg white + label .c.tr.$addr.t -text $text -font $codeFont + pack .c.tr.$addr.a .c.tr.$addr.i .c.tr.$addr.s \ + .c.tr.$addr.t -in .c.tr.$addr -side left +} + +# Keep track of which instructions have stage labels + +set oldAddr {} + +proc simLabel {addrs labs} { + global oldAddr + set newAddr {} + # Clear away any old labels + foreach a $oldAddr { + .c.tr.$a.s config -text "" + } + for {set i 0} {$i < [llength $addrs]} {incr i} { + set a [lindex $addrs $i] + set t [lindex $labs $i] + if {[winfo exists .c.tr.$a]} { + .c.tr.$a.s config -text $t + set newAddr [concat $newAddr $a] + } + } + set oldAddr $newAddr +} + +proc simResetAll {} { + global simStat + set simStat "AOK" + simReset + clearMem + simLabel {} {} +} + +############################################################################### +# Memory Display # +############################################################################### +toplevel .m +wm title .m "Memory Contents" +frame .m.t +pack .m.t -in .m -side top -anchor w + +label .m.t.lab -width 6 -font $dpyFont -text " " +pack .m.t.lab -in .m.t -side left +for {set i 0} {$i < 16} {incr i 8} { + label .m.t.a$i -width 16 -font $dpyFont -text [format " 0x---%x" [expr $i % 16]] + pack .m.t.a$i -in .m.t -side left +} + + +# Keep track of range of addresses currently displayed +set minAddr 0 +set memCnt 0 +set haveMem 0 + +proc createMem {nminAddr nmemCnt} { + global minAddr memCnt haveMem codeFont dpyFont normalBg + set minAddr $nminAddr + set memCnt $nmemCnt + + if { $haveMem } { destroy .m.e } + + # Create Memory Structure + frame .m.e + set haveMem 1 + pack .m.e -in .m -side top -anchor w + # Now fill it with values + for {set i 0} {$i < $memCnt} {incr i 16} { + set addr [expr $minAddr + $i] + + frame .m.e.r$i + pack .m.e.r$i -side bottom -in .m.e + label .m.e.r$i.lab -width 6 -font $dpyFont -text [format "0x%.3x-" [expr $addr / 16]] + pack .m.e.r$i.lab -in .m.e.r$i -side left + + for {set j 0} {$j < 16} {incr j 8} { + set a [expr $addr + $j] + label .m.e.v$a -width 16 -font $dpyFont -relief ridge \ + -bg $normalBg + pack .m.e.v$a -in .m.e.r$i -side left + } + } +} + +proc setMem {Addr Val} { + global minAddr memCnt + if {$Addr < $minAddr || $Addr > [expr $minAddr + $memCnt]} { + error "Memory address $Addr out of range" + } + .m.e.v$Addr config -text [format %16x $Val] +} + +proc clearMem {} { + destroy .m.e + createMem 0 0 +} + + + +############################################################################### +# Command Line Initialization # +############################################################################### + +# Get code file name from input + +# Find file with specified extension +proc findFile {tlist ext} { + foreach t $tlist { + if {[string match "*.$ext" $t]} {return $t} + } + return "" +} + + +set codeFile [findFile $argv yo] +if {$codeFile != ""} { loadCode $codeFile} diff --git a/arch/sim/pipe/pipeline.h b/arch/sim/pipe/pipeline.h new file mode 100644 index 0000000..61eb239 --- /dev/null +++ b/arch/sim/pipe/pipeline.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * pipe.h + * + * Code for implementing pipelined processor simulators + ******************************************************************************/ + +#ifndef PIPE_H +#define PIPE_H + +/****************************************************************************** + * #includes + ******************************************************************************/ + +#include + +/****************************************************************************** + * typedefs + ******************************************************************************/ + +/* Different control operations for pipeline register */ +/* LOAD: Copy next state to current */ +/* STALL: Keep current state unchanged */ +/* BUBBLE: Set current state to nop */ +/* ERROR: Occurs when both stall & load signals set */ + +typedef enum { P_LOAD, P_STALL, P_BUBBLE, P_ERROR } p_stat_t; + +typedef struct { + /* Current and next register state */ + void *current; + void *next; + /* Contents of register when bubble occurs */ + void *bubble_val; + /* Number of state bytes */ + int count; + /* How should state be updated next time? */ + p_stat_t op; +} pipe_ele, *pipe_ptr; + +/****************************************************************************** + * function declarations + ******************************************************************************/ + +/* Create new pipe with count bytes of state */ +/* bubble_val indicates state corresponding to pipeline bubble */ +pipe_ptr new_pipe(int count, void *bubble_val); + +/* Update all pipes */ +void update_pipes(); + +/* Set all pipes to bubble values */ +void clear_pipes(); + +/* Utility code */ + +/* Print hex/oct/binary format with leading zeros */ +/* bpd denotes bits per digit Should be in range 1-4, + bpw denotes bits per word.*/ +void wprint(uword_t x, int bpd, int bpw, FILE *fp); +void wstring(uword_t x, int bpd, int bpw, char *s); + +/******************************************************************************/ + +#endif /* PIPE_H */ + + + diff --git a/arch/sim/pipe/psim.c b/arch/sim/pipe/psim.c new file mode 100644 index 0000000..c08508e --- /dev/null +++ b/arch/sim/pipe/psim.c @@ -0,0 +1,1604 @@ +/************************************************************************** + * psim.c - Pipelined Y86-64 simulator + * + * Copyright (c) 2010, 2015. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + **************************************************************************/ + +#include +#include +#include +#include +#include + +#include "isa.h" +#include "pipeline.h" +#include "stages.h" +#include "sim.h" + +#define MAXBUF 1024 +#define DEFAULTNAME "Y86-64 Simulator: " + +#ifdef HAS_GUI +#include +#endif /* HAS_GUI */ + +#define MAXARGS 128 +#define MAXBUF 1024 +#define TKARGS 3 + + +/*************** + * Begin Globals + ***************/ + +/* Simulator name defined and initialized by the compiled HCL file */ +/* according to the -n argument supplied to hcl2c */ +extern char simname[]; + +/* Parameters modifed by the command line */ +int gui_mode = FALSE; /* Run in GUI mode instead of TTY mode? (-g) */ +char *object_filename; /* The input object file name. */ +FILE *object_file; /* Input file handle */ +bool_t verbosity = 2; /* Verbosity level [TTY only] (-v) */ +word_t instr_limit = 10000; /* Instruction limit [TTY only] (-l) */ +bool_t do_check = FALSE; /* Test with ISA simulator? [TTY only] (-t) */ + +/************* + * End Globals + *************/ + + +/*************************** + * Begin function prototypes + ***************************/ + +word_t sim_run_pipe(word_t max_instr, word_t max_cycle, byte_t *statusp, cc_t *ccp); +static void usage(char *name); /* Print helpful usage message */ +static void run_tty_sim(); /* Run simulator in TTY mode */ + +#ifdef HAS_GUI +void addAppCommands(Tcl_Interp *interp); /* Add application-dependent commands */ +#endif /* HAS_GUI */ + +/************************* + * End function prototypes + *************************/ + + +/******************************************************************* + * Part 1: This part is the initial entry point that handles general + * initialization. It parses the command line and does any necessary + * setup to run in either TTY or GUI mode, and then starts the + * simulation. + *******************************************************************/ + +/* + * sim_main - main simulator routine. This function is called from the + * main() routine in the HCL file. + */ +int sim_main(int argc, char **argv) +{ + int i; + int c; + char *myargv[MAXARGS]; + + /* Parse the command line arguments */ + while ((c = getopt(argc, argv, "htgl:v:")) != -1) { + switch(c) { + case 'h': + usage(argv[0]); + break; + case 'l': + instr_limit = atoll(optarg); + break; + case 'v': + verbosity = atoi(optarg); + if (verbosity < 0 || verbosity > 2) { + printf("Invalid verbosity %d\n", verbosity); + usage(argv[0]); + } + break; + case 't': + do_check = TRUE; + break; + case 'g': + gui_mode = TRUE; + break; + default: + printf("Invalid option '%c'\n", c); + usage(argv[0]); + break; + } + } + + + /* Do we have too many arguments? */ + if (optind < argc - 1) { + printf("Too many command line arguments:"); + for (i = optind; i < argc; i++) + printf(" %s", argv[i]); + printf("\n"); + usage(argv[0]); + } + + + /* The single unflagged argument should be the object file name */ + object_filename = NULL; + object_file = NULL; + if (optind < argc) { + object_filename = argv[optind]; + object_file = fopen(object_filename, "r"); + if (!object_file) { + fprintf(stderr, "Couldn't open object file %s\n", object_filename); + exit(1); + } + } + + + /* Run the simulator in GUI mode (-g flag) */ + if (gui_mode) { + +#ifndef HAS_GUI + printf("To run in GUI mode, you must recompile with the HAS_GUI constant defined.\n"); + exit(1); +#endif /* HAS_GUI */ + + /* In GUI mode, we must specify the object file on command line */ + if (!object_file) { + printf("Missing object file argument in GUI mode\n"); + usage(argv[0]); + } + + /* Build the command line for the GUI simulator */ + for (i = 0; i < TKARGS; i++) { + if ((myargv[i] = malloc(MAXBUF*sizeof(char))) == NULL) { + perror("malloc error"); + exit(1); + } + } + strcpy(myargv[0], argv[0]); + strcpy(myargv[1], "pipe.tcl"); + strcpy(myargv[2], object_filename); + myargv[3] = NULL; + + /* Start the GUI simulator */ +#ifdef HAS_GUI + Tk_Main(TKARGS, myargv, Tcl_AppInit); +#endif /* HAS_GUI */ + exit(0); + } + + /* Otherwise, run the simulator in TTY mode (no -g flag) */ + run_tty_sim(); + + exit(0); +} + +/* + * run_tty_sim - Run the simulator in TTY mode + */ +static void run_tty_sim() +{ + word_t icount = 0; + byte_t run_status = STAT_AOK; + cc_t result_cc = 0; + word_t byte_cnt = 0; + mem_t mem0, reg0; + state_ptr isa_state = NULL; + + + /* In TTY mode, the default object file comes from stdin */ + if (!object_file) { + object_file = stdin; + } + + if (verbosity >= 2) + sim_set_dumpfile(stdout); + sim_init(); + + /* Emit simulator name */ + if (verbosity >= 2) + printf("%s\n", simname); + + byte_cnt = load_mem(mem, object_file, 1); + if (byte_cnt == 0) { + fprintf(stderr, "No lines of code found\n"); + exit(1); + } else if (verbosity >= 2) { + printf("%lld bytes of code read\n", byte_cnt); + } + fclose(object_file); + if (do_check) { + isa_state = new_state(0); + free_mem(isa_state->r); + free_mem(isa_state->m); + isa_state->m = copy_mem(mem); + isa_state->r = copy_mem(reg); + isa_state->cc = cc; + } + + mem0 = copy_mem(mem); + reg0 = copy_mem(reg); + + icount = sim_run_pipe(instr_limit, 5*instr_limit, &run_status, &result_cc); + if (verbosity > 0) { + printf("%lld instructions executed\n", icount); + printf("Status = %s\n", stat_name(run_status)); + printf("Condition Codes: %s\n", cc_name(result_cc)); + printf("Changed Register State:\n"); + diff_reg(reg0, reg, stdout); + printf("Changed Memory State:\n"); + diff_mem(mem0, mem, stdout); + } + if (do_check) { + byte_t e = STAT_AOK; + word_t step; + bool_t match = TRUE; + + for (step = 0; step < instr_limit && e == STAT_AOK; step++) { + e = step_state(isa_state, stdout); + } + + if (diff_reg(isa_state->r, reg, NULL)) { + match = FALSE; + if (verbosity > 0) { + printf("ISA Register != Pipeline Register File\n"); + diff_reg(isa_state->r, reg, stdout); + } + } + if (diff_mem(isa_state->m, mem, NULL)) { + match = FALSE; + if (verbosity > 0) { + printf("ISA Memory != Pipeline Memory\n"); + diff_mem(isa_state->m, mem, stdout); + } + } + if (isa_state->cc != result_cc) { + match = FALSE; + if (verbosity > 0) { + printf("ISA Cond. Codes (%s) != Pipeline Cond. Codes (%s)\n", + cc_name(isa_state->cc), cc_name(result_cc)); + } + } + if (match) { + printf("ISA Check Succeeds\n"); + } else { + printf("ISA Check Fails\n"); + } + } + + /* Emit CPI statistics */ + { + double cpi = instructions > 0 ? (double) cycles/instructions : 1.0; + printf("CPI: %lld cycles/%lld instructions = %.2f\n", + cycles, instructions, cpi); + } + +} + +/* + * usage - print helpful diagnostic information + */ +static void usage(char *name) +{ + printf("Usage: %s [-htg] [-l m] [-v n] file.yo\n", name); + printf("file.yo arg required in GUI mode, optional in TTY mode (default stdin)\n"); + printf(" -h Print this message\n"); + printf(" -g Run in GUI mode instead of TTY mode (default TTY)\n"); + printf(" -l m Set instruction limit to m [TTY mode only] (default %lld)\n", instr_limit); + printf(" -v n Set verbosity level to 0 <= n <= 2 [TTY mode only] (default %d)\n", verbosity); + printf(" -t Test result against ISA simulator [TTY mode only]\n"); + exit(0); +} + + +/********************************************************* + * Part 2: This part contains the core simulator routines. + *********************************************************/ + + +/***************** + * Part 2 Globals + *****************/ + +/* Performance monitoring */ +/* How many cycles have been simulated? */ +word_t cycles = 0; +/* How many instructions have passed through the WB stage? */ +word_t instructions = 0; + +/* Has simulator gotten past initial bubbles? */ +static int starting_up = 1; + + + +/* Both instruction and data memory */ +mem_t mem; +word_t minAddr = 0; +word_t memCnt = 0; + +/* Register file */ +mem_t reg; +/* Condition code register */ +cc_t cc; +/* Status code */ +stat_t status; + + +/* Pending updates to state */ +word_t cc_in = DEFAULT_CC; +word_t wb_destE = REG_NONE; +word_t wb_valE = 0; +word_t wb_destM = REG_NONE; +word_t wb_valM = 0; +word_t mem_addr = 0; +word_t mem_data = 0; +bool_t mem_write = FALSE; + +/* EX Operand sources */ +mux_source_t amux = MUX_NONE; +mux_source_t bmux = MUX_NONE; + +/* Current and next states of all pipeline registers */ +pc_ptr pc_curr; +if_id_ptr if_id_curr; +id_ex_ptr id_ex_curr; +ex_mem_ptr ex_mem_curr; +mem_wb_ptr mem_wb_curr; + +pc_ptr pc_next; +if_id_ptr if_id_next; +id_ex_ptr id_ex_next; +ex_mem_ptr ex_mem_next; +mem_wb_ptr mem_wb_next; + +/* Intermediate values */ +word_t f_pc; +byte_t imem_icode; +byte_t imem_ifun; +bool_t imem_error; +bool_t instr_valid; +word_t d_regvala; +word_t d_regvalb; +word_t e_vala; +word_t e_valb; +bool_t e_bcond; +bool_t dmem_error; + +/* The pipeline state */ +pipe_ptr pc_state, if_id_state, id_ex_state, ex_mem_state, mem_wb_state; + +/* Simulator operating mode */ +sim_mode_t sim_mode = S_FORWARD; +/* Log file */ +FILE *dumpfile = NULL; + +/***************************************************************************** + * reporting code + *****************************************************************************/ + +#ifdef HAS_GUI +/* used for formatting instructions */ +static char status_msg[128]; + +static char *format_pc(pc_ptr state) +{ + char pstring[17]; + wstring(state->pc, 4, 64, pstring); + sprintf(status_msg, "%s %s", stat_name(state->status), pstring); + return status_msg; +} + +static char *format_if_id(if_id_ptr state) +{ + char valcstring[17]; + char valpstring[17]; + wstring(state->valc, 4, 64, valcstring); + wstring(state->valp, 4, 64, valpstring); + sprintf(status_msg, "%s %s %s %s %s %s", + stat_name(state->status), + iname(HPACK(state->icode,state->ifun)), + reg_name(state->ra), + reg_name(state->rb), + valcstring, + valpstring); + return status_msg; +} + +static char *format_id_ex(id_ex_ptr state) +{ + char valcstring[17]; + char valastring[17]; + char valbstring[17]; + wstring(state->valc, 4, 64, valcstring); + wstring(state->vala, 4, 64, valastring); + wstring(state->valb, 4, 64, valbstring); + sprintf(status_msg, "%s %s %s %s %s %s %s %s %s", + stat_name(state->status), + iname(HPACK(state->icode, state->ifun)), + valcstring, + valastring, + valbstring, + reg_name(state->deste), + reg_name(state->destm), + reg_name(state->srca), + reg_name(state->srcb)); + return status_msg; +} + +static char *format_ex_mem(ex_mem_ptr state) +{ + char valestring[17]; + char valastring[17]; + wstring(state->vale, 4, 64, valestring); + wstring(state->vala, 4, 64, valastring); + sprintf(status_msg, "%s %s %c %s %s %s %s", + stat_name(state->status), + iname(HPACK(state->icode, state->ifun)), + state->takebranch ? 'Y' : 'N', + valestring, + valastring, + reg_name(state->deste), + reg_name(state->destm)); + + return status_msg; +} + +static char *format_mem_wb(mem_wb_ptr state) +{ + char valestring[17]; + char valmstring[17]; + wstring(state->vale, 4, 64, valestring); + wstring(state->valm, 4, 64, valmstring); + sprintf(status_msg, "%s %s %s %s %s %s", + stat_name(state->status), + iname(HPACK(state->icode, state->ifun)), + valestring, + valmstring, + reg_name(state->deste), + reg_name(state->destm)); + + return status_msg; +} +#endif /* HAS_GUI */ + +/* Report system state */ +static void sim_report() +{ + +#ifdef HAS_GUI + if (gui_mode) { + report_pc(f_pc, pc_curr->status != STAT_BUB, + if_id_curr->stage_pc, if_id_curr->status != STAT_BUB, + id_ex_curr->stage_pc, id_ex_curr->status != STAT_BUB, + ex_mem_curr->stage_pc, ex_mem_curr->status != STAT_BUB, + mem_wb_curr->stage_pc, mem_wb_curr->status != STAT_BUB); + report_state("F", 0, format_pc(pc_next)); + report_state("F", 1, format_pc(pc_curr)); + report_state("D", 0, format_if_id(if_id_next)); + report_state("D", 1, format_if_id(if_id_curr)); + report_state("E", 0, format_id_ex(id_ex_next)); + report_state("E", 1, format_id_ex(id_ex_curr)); + report_state("M", 0, format_ex_mem(ex_mem_next)); + report_state("M", 1, format_ex_mem(ex_mem_curr)); + report_state("W", 0, format_mem_wb(mem_wb_next)); + report_state("W", 1, format_mem_wb(mem_wb_curr)); + /* signal_sources(); */ + show_cc(cc); + show_stat(status); + show_cpi(); + } +#endif + +} + +/***************************************************************************** + * pipeline control + * These functions can be used to handle hazards + *****************************************************************************/ + +/* bubble stage (has effect at next update) */ +void sim_bubble_stage(stage_id_t stage) +{ + switch (stage) + { + case IF_STAGE : pc_state->op = P_BUBBLE; break; + case ID_STAGE : if_id_state->op = P_BUBBLE; break; + case EX_STAGE : id_ex_state->op = P_BUBBLE; break; + case MEM_STAGE: ex_mem_state->op = P_BUBBLE; break; + case WB_STAGE : mem_wb_state->op = P_BUBBLE; break; + } +} + +/* stall stage (has effect at next update) */ +void sim_stall_stage(stage_id_t stage) { + switch (stage) + { + case IF_STAGE : pc_state->op = P_STALL; break; + case ID_STAGE : if_id_state->op = P_STALL; break; + case EX_STAGE : id_ex_state->op = P_STALL; break; + case MEM_STAGE: ex_mem_state->op = P_STALL; break; + case WB_STAGE : mem_wb_state->op = P_STALL; break; + } +} + + +static int initialized = 0; + +void sim_init() +{ + /* Create memory and register files */ + initialized = 1; + mem = init_mem(MEM_SIZE); + reg = init_reg(); + + /* create 5 pipe registers */ + pc_state = new_pipe(sizeof(pc_ele), (void *) &bubble_pc); + if_id_state = new_pipe(sizeof(if_id_ele), (void *) &bubble_if_id); + id_ex_state = new_pipe(sizeof(id_ex_ele), (void *) &bubble_id_ex); + ex_mem_state = new_pipe(sizeof(ex_mem_ele), (void *) &bubble_ex_mem); + mem_wb_state = new_pipe(sizeof(mem_wb_ele), (void *) &bubble_mem_wb); + + /* connect them to the pipeline stages */ + pc_next = pc_state->next; + pc_curr = pc_state->current; + + if_id_next = if_id_state->next; + if_id_curr = if_id_state->current; + + id_ex_next = id_ex_state->next; + id_ex_curr = id_ex_state->current; + + ex_mem_next = ex_mem_state->next; + ex_mem_curr = ex_mem_state->current; + + mem_wb_next = mem_wb_state->next; + mem_wb_curr = mem_wb_state->current; + + sim_reset(); + clear_mem(mem); +} + +void sim_reset() +{ + if (!initialized) + sim_init(); + clear_pipes(); + clear_mem(reg); + minAddr = 0; + memCnt = 0; + starting_up = 1; + cycles = instructions = 0; + cc = DEFAULT_CC; + status = STAT_AOK; + +#ifdef HAS_GUI + if (gui_mode) { + signal_register_clear(); + create_memory_display(); + } +#endif + + amux = bmux = MUX_NONE; + cc = cc_in = DEFAULT_CC; + wb_destE = REG_NONE; + wb_valE = 0; + wb_destM = REG_NONE; + wb_valM = 0; + mem_addr = 0; + mem_data = 0; + mem_write = FALSE; + sim_report(); +} + +/* Update state elements */ +/* May need to disable updating of memory & condition codes */ +static void update_state(bool_t update_mem, bool_t update_cc) +{ + /* Writeback(s): + If either register is REG_NONE, write will have no effect . + Order of two writes determines semantics of + popl %rsp. According to ISA, %rsp will get popped value + */ + + if (wb_destE != REG_NONE) { + sim_log("\tWriteback: Wrote 0x%llx to register %s\n", + wb_valE, reg_name(wb_destE)); + set_reg_val(reg, wb_destE, wb_valE); + } + if (wb_destM != REG_NONE) { + sim_log("\tWriteback: Wrote 0x%llx to register %s\n", + wb_valM, reg_name(wb_destM)); + set_reg_val(reg, wb_destM, wb_valM); + } + + /* Memory write */ + if (mem_write && !update_mem) { + sim_log("\tDisabled write of 0x%llx to address 0x%llx\n", mem_data, mem_addr); + } + if (update_mem && mem_write) { + if (!set_word_val(mem, mem_addr, mem_data)) { + sim_log("\tCouldn't write to address 0x%llx\n", mem_addr); + } else { + sim_log("\tWrote 0x%llx to address 0x%llx\n", mem_data, mem_addr); + +#ifdef HAS_GUI + if (gui_mode) { + if (mem_addr % 8 != 0) { + /* Just did a misaligned write. + Need to display both words */ + word_t align_addr = mem_addr & ~0x3; + word_t val; + get_word_val(mem, align_addr, &val); + set_memory(align_addr, val); + align_addr+=8; + get_word_val(mem, align_addr, &val); + set_memory(align_addr, val); + } else { + set_memory(mem_addr, mem_data); + } + } +#endif + + } + } + if (update_cc) + cc = cc_in; +} + +/* Text representation of status */ +void tty_report(word_t cyc) { + sim_log("\nCycle %lld. CC=%s, Stat=%s\n", cyc, cc_name(cc), stat_name(status)); + + sim_log("F: predPC = 0x%llx\n", pc_curr->pc); + + sim_log("D: instr = %s, rA = %s, rB = %s, valC = 0x%llx, valP = 0x%llx, Stat = %s\n", + iname(HPACK(if_id_curr->icode, if_id_curr->ifun)), + reg_name(if_id_curr->ra), reg_name(if_id_curr->rb), + if_id_curr->valc, if_id_curr->valp, + stat_name(if_id_curr->status)); + + sim_log("E: instr = %s, valC = 0x%llx, valA = 0x%llx, valB = 0x%llx\n srcA = %s, srcB = %s, dstE = %s, dstM = %s, Stat = %s\n", + iname(HPACK(id_ex_curr->icode, id_ex_curr->ifun)), + id_ex_curr->valc, id_ex_curr->vala, id_ex_curr->valb, + reg_name(id_ex_curr->srca), reg_name(id_ex_curr->srcb), + reg_name(id_ex_curr->deste), reg_name(id_ex_curr->destm), + stat_name(id_ex_curr->status)); + + sim_log("M: instr = %s, Cnd = %d, valE = 0x%llx, valA = 0x%llx\n dstE = %s, dstM = %s, Stat = %s\n", + iname(HPACK(ex_mem_curr->icode, ex_mem_curr->ifun)), + ex_mem_curr->takebranch, + ex_mem_curr->vale, ex_mem_curr->vala, + reg_name(ex_mem_curr->deste), reg_name(ex_mem_curr->destm), + stat_name(ex_mem_curr->status)); + + sim_log("W: instr = %s, valE = 0x%llx, valM = 0x%llx, dstE = %s, dstM = %s, Stat = %s\n", + iname(HPACK(mem_wb_curr->icode, mem_wb_curr->ifun)), + mem_wb_curr->vale, mem_wb_curr->valm, + reg_name(mem_wb_curr->deste), reg_name(mem_wb_curr->destm), + stat_name(mem_wb_curr->status)); +} + +/* Run pipeline for one cycle */ +/* Return status of processor */ +/* Max_instr indicates maximum number of instructions that + want to complete during this simulation run. */ +static byte_t sim_step_pipe(word_t max_instr, word_t ccount) +{ + byte_t wb_status = mem_wb_curr->status; + byte_t mem_status = mem_wb_next->status; + /* How many instructions are ahead of one in wb / ex? */ + int ahead_mem = (wb_status != STAT_BUB); + int ahead_ex = ahead_mem + (mem_status != STAT_BUB); + bool_t update_mem = ahead_mem < max_instr; + bool_t update_cc = ahead_ex < max_instr; + + /* Update program-visible state */ + update_state(update_mem, update_cc); + /* Update pipe registers */ + update_pipes(); + tty_report(ccount); + if (pc_state->op == P_ERROR) + pc_curr->status = STAT_PIP; + if (if_id_state->op == P_ERROR) + if_id_curr->status = STAT_PIP; + if (id_ex_state->op == P_ERROR) + id_ex_curr->status = STAT_PIP; + if (ex_mem_state->op == P_ERROR) + ex_mem_curr->status = STAT_PIP; + if (mem_wb_state->op == P_ERROR) + mem_wb_curr->status = STAT_PIP; + + /* Need to do decode after execute & memory stages, + and memory stage before execute, in order to propagate + forwarding values properly */ + do_if_stage(); + do_mem_stage(); + do_ex_stage(); + do_id_wb_stages(); + + do_stall_check(); +#if 0 + /* This doesn't seem necessary */ + if (id_ex_curr->status != STAT_AOK + && id_ex_curr->status != STAT_BUB) { + if_id_state->op = P_BUBBLE; + id_ex_state->op = P_BUBBLE; + } +#endif + + /* Performance monitoring */ + if (mem_wb_curr->status != STAT_BUB && mem_wb_curr->icode != I_POP2) { + starting_up = 0; + instructions++; + cycles++; + } else { + if (!starting_up) + cycles++; + } + + sim_report(); + return status; +} + +/* + Run pipeline until one of following occurs: + - An error status is encountered in WB. + - max_instr instructions have completed through WB + - max_cycle cycles have been simulated + + Return number of instructions executed. + if statusp nonnull, then will be set to status of final instruction + if ccp nonnull, then will be set to condition codes of final instruction +*/ +word_t sim_run_pipe(word_t max_instr, word_t max_cycle, byte_t *statusp, cc_t *ccp) +{ + word_t icount = 0; + word_t ccount = 0; + byte_t run_status = STAT_AOK; + while (icount < max_instr && ccount < max_cycle) { + run_status = sim_step_pipe(max_instr-icount, ccount); + if (run_status != STAT_BUB) + icount++; + if (run_status != STAT_AOK && run_status != STAT_BUB) + break; + ccount++; + } + if (statusp) + *statusp = run_status; + if (ccp) + *ccp = cc; + return icount; +} + +/* If dumpfile set nonNULL, lots of status info printed out */ +void sim_set_dumpfile(FILE *df) +{ + dumpfile = df; +} + +/* + * sim_log dumps a formatted string to the dumpfile, if it exists + * accepts variable argument list + */ +void sim_log( const char *format, ... ) { + if (dumpfile) { + va_list arg; + va_start( arg, format ); + vfprintf( dumpfile, format, arg ); + va_end( arg ); + } +} + + +/************************************************************* + * Part 3: This part contains support for the GUI simulator + *************************************************************/ + +#ifdef HAS_GUI + +/********************** + * Begin Part 3 globals + **********************/ + +/* Hack for SunOS */ +extern int matherr(); +int *tclDummyMathPtr = (int *) matherr; + +static char tcl_msg[256]; + +/* Keep track of the TCL Interpreter */ +static Tcl_Interp *sim_interp = NULL; + +static mem_t post_load_mem; + +/********************** + * End Part 3 globals + **********************/ + + +/****************************************************************************** + * function declarations + ******************************************************************************/ + +int simResetCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); + +int simLoadCodeCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); + +int simLoadDataCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); + +int simRunCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); + +int simModeCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); + +void addAppCommands(Tcl_Interp *interp); + + +/****************************************************************************** + * tcl command definitions + ******************************************************************************/ + +/* Implement command versions of the simulation functions */ +int simResetCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + sim_interp = interp; + if (argc != 1) { + interp->result = "No arguments allowed"; + return TCL_ERROR; + } + sim_reset(); + if (post_load_mem) { + free_mem(mem); + mem = copy_mem(post_load_mem); + } + interp->result = stat_name(STAT_AOK); + return TCL_OK; +} + +int simLoadCodeCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + FILE *code_file; + word_t code_count; + sim_interp = interp; + if (argc != 2) { + interp->result = "One argument required"; + return TCL_ERROR; + } + code_file = fopen(argv[1], "r"); + if (!code_file) { + sprintf(tcl_msg, "Couldn't open code file '%s'", argv[1]); + interp->result = tcl_msg; + return TCL_ERROR; + } + sim_reset(); + code_count = load_mem(mem, code_file, 0); + post_load_mem = copy_mem(mem); + sprintf(tcl_msg, "%lld", code_count); + interp->result = tcl_msg; + fclose(code_file); + return TCL_OK; +} + +int simLoadDataCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + FILE *data_file; + word_t word_count = 0; + interp->result = "Not implemented"; + return TCL_ERROR; + + + sim_interp = interp; + if (argc != 2) { + interp->result = "One argument required"; + return TCL_ERROR; + } + data_file = fopen(argv[1], "r"); + if (!data_file) { + sprintf(tcl_msg, "Couldn't open data file '%s'", argv[1]); + interp->result = tcl_msg; + return TCL_ERROR; + } + sprintf(tcl_msg, "%lld", word_count); + interp->result = tcl_msg; + fclose(data_file); + return TCL_OK; +} + + +int simRunCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + word_t cycle_limit = 1; + byte_t status; + cc_t cc; + sim_interp = interp; + if (argc > 2) { + interp->result = "At most one argument allowed"; + return TCL_ERROR; + } + if (argc >= 2 && + (sscanf(argv[1], "%lld", &cycle_limit) != 1 || + cycle_limit < 0)) { + sprintf(tcl_msg, "Cannot run for '%s' cycles!", argv[1]); + interp->result = tcl_msg; + return TCL_ERROR; + } + sim_run_pipe(cycle_limit + 5, cycle_limit, &status, &cc); + interp->result = stat_name(status); + return TCL_OK; +} + +int simModeCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + sim_interp = interp; + if (argc != 2) { + interp->result = "One argument required"; + return TCL_ERROR; + } + interp->result = argv[1]; + if (strcmp(argv[1], "wedged") == 0) + sim_mode = S_WEDGED; + else if (strcmp(argv[1], "stall") == 0) + sim_mode = S_STALL; + else if (strcmp(argv[1], "forward") == 0) + sim_mode = S_FORWARD; + else { + sprintf(tcl_msg, "Unknown mode '%s'", argv[1]); + interp->result = tcl_msg; + return TCL_ERROR; + } + return TCL_OK; +} + + +/****************************************************************************** + * registering the commands with tcl + ******************************************************************************/ + +void addAppCommands(Tcl_Interp *interp) +{ + sim_interp = interp; + Tcl_CreateCommand(interp, "simReset", (Tcl_CmdProc *) simResetCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "simCode", (Tcl_CmdProc *) simLoadCodeCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "simData", (Tcl_CmdProc *) simLoadDataCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "simRun", (Tcl_CmdProc *) simRunCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "setSimMode", (Tcl_CmdProc *) simModeCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); +} + +/****************************************************************************** + * tcl functionality called from within C + ******************************************************************************/ + +/* Provide mechanism for simulator to update register display */ +void signal_register_update(reg_id_t r, word_t val) { + int code; + sprintf(tcl_msg, "setReg %d %lld 1", (int) r, (word_t) val); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to signal register set\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Provide mechanism for simulator to generate memory display */ +void create_memory_display() { + int code; + sprintf(tcl_msg, "createMem %lld %lld", minAddr, memCnt); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Command '%s' failed\n", tcl_msg); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } else { + word_t i; + for (i = 0; i < memCnt && code == TCL_OK; i+=8) { + word_t addr = minAddr+i; + word_t val; + if (!get_word_val(mem, addr, &val)) { + fprintf(stderr, "Out of bounds memory display\n"); + return; + } + sprintf(tcl_msg, "setMem %lld %lld", addr, val); + code = Tcl_Eval(sim_interp, tcl_msg); + } + if (code != TCL_OK) { + fprintf(stderr, "Couldn't set memory value\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } + } +} + +/* Provide mechanism for simulator to update memory value */ +void set_memory(word_t addr, word_t val) { + int code; + word_t nminAddr = minAddr; + word_t nmemCnt = memCnt; + + /* First see if we need to expand memory range */ + if (memCnt == 0) { + nminAddr = addr; + nmemCnt = 8; + } else if (addr < minAddr) { + nminAddr = addr; + nmemCnt = minAddr + memCnt - addr; + } else if (addr >= minAddr+memCnt) { + nmemCnt = addr-minAddr+8; + } + /* Now make sure nminAddr & nmemCnt are multiples of 16 */ + nmemCnt = ((nminAddr & 0xF) + nmemCnt + 0xF) & ~0xF; + nminAddr = nminAddr & ~0xF; + + if (nminAddr != minAddr || nmemCnt != memCnt) { + minAddr = nminAddr; + memCnt = nmemCnt; + create_memory_display(); + } else { + sprintf(tcl_msg, "setMem %lld %lld", addr, val); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Couldn't set memory value 0x%llx to 0x%llx\n", + addr, val); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } + } +} + +/* Provide mechanism for simulator to update condition code display */ +void show_cc(cc_t cc) +{ + int code; + sprintf(tcl_msg, "setCC %d %d %d", + GET_ZF(cc), GET_SF(cc), GET_OF(cc)); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to display condition codes\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Provide mechanism for simulator to update status display */ +void show_stat(stat_t stat) +{ + int code; + sprintf(tcl_msg, "showStat %s", stat_name(stat)); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to display status\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + + + +/* Provide mechanism for simulator to update performance information */ +void show_cpi() { + int code; + double cpi = instructions > 0 ? (double) cycles/instructions : 1.0; + sprintf(tcl_msg, "showCPI %lld %lld %.2f", + cycles, instructions, (double) cpi); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to display CPI\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +char *rname[] = {"none", "ea", "eb", "me", "wm", "we"}; + +/* provide mechanism for simulator to specify source registers */ +void signal_sources() { + int code; + sprintf(tcl_msg, "showSources %s %s", + rname[amux], rname[bmux]); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to signal forwarding sources\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Provide mechanism for simulator to clear register display */ +void signal_register_clear() { + int code; + code = Tcl_Eval(sim_interp, "clearReg"); + if (code != TCL_OK) { + fprintf(stderr, "Failed to signal register clear\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Provide mechanism for simulator to report instructions as they are + read in +*/ + +void report_line(word_t line_no, word_t addr, char *hex, char *text) { + int code; + sprintf(tcl_msg, "addCodeLine %lld %lld {%s} {%s}", line_no, addr, hex, text); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to report code line 0x%llx\n", addr); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + + +/* Provide mechanism for simulator to report which instructions are in + which stages */ +void report_pc(unsigned fpc, unsigned char fpcv, + unsigned dpc, unsigned char dpcv, + unsigned epc, unsigned char epcv, + unsigned mpc, unsigned char mpcv, + unsigned wpc, unsigned char wpcv) +{ + int status; + char addr[10]; + char code[12]; + Tcl_DString cmd; + Tcl_DStringInit(&cmd); + Tcl_DStringAppend(&cmd, "simLabel ", -1); + Tcl_DStringStartSublist(&cmd); + if (fpcv) { + sprintf(addr, "%u", fpc); + Tcl_DStringAppendElement(&cmd, addr); + } + if (dpcv) { + sprintf(addr, "%u", dpc); + Tcl_DStringAppendElement(&cmd, addr); + } + if (epcv) { + sprintf(addr, "%u", epc); + Tcl_DStringAppendElement(&cmd, addr); + } + if (mpcv) { + sprintf(addr, "%u", mpc); + Tcl_DStringAppendElement(&cmd, addr); + } + if (wpcv) { + sprintf(addr, "%u", wpc); + Tcl_DStringAppendElement(&cmd, addr); + } + Tcl_DStringEndSublist(&cmd); + Tcl_DStringStartSublist(&cmd); + sprintf(code, "%s %s %s %s %s", + fpcv ? "F" : "", + dpcv ? "D" : "", + epcv ? "E" : "", + mpcv ? "M" : "", + wpcv ? "W" : ""); + Tcl_DStringAppend(&cmd, code, -1); + Tcl_DStringEndSublist(&cmd); + /* Debug + fprintf(stderr, "Code '%s'\n", Tcl_DStringValue(&cmd)); + */ + status = Tcl_Eval(sim_interp, Tcl_DStringValue(&cmd)); + if (status != TCL_OK) { + fprintf(stderr, "Failed to report pipe code '%s'\n", code); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Report single line of pipeline state */ +void report_state(char *id, word_t current, char *txt) +{ + int status; + sprintf(tcl_msg, "updateStage %s %lld {%s}", id, current,txt); + status = Tcl_Eval(sim_interp, tcl_msg); + if (status != TCL_OK) { + fprintf(stderr, "Failed to report pipe status\n"); + fprintf(stderr, "\tStage %s.%s, status '%s'\n", + id, current ? "current" : "next", txt); + fprintf(stderr, "\tError Message was '%s'\n", sim_interp->result); + } +} + + +/* + * Tcl_AppInit - Called by TCL to perform application-specific initialization. + */ +int Tcl_AppInit(Tcl_Interp *interp) +{ + /* Tell TCL about the name of the simulator so it can */ + /* use it as the title of the main window */ + Tcl_SetVar(interp, "simname", simname, TCL_GLOBAL_ONLY); + + if (Tcl_Init(interp) == TCL_ERROR) + return TCL_ERROR; + if (Tk_Init(interp) == TCL_ERROR) + return TCL_ERROR; + Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit); + + /* Call procedure to add new commands */ + addAppCommands(interp); + + /* + * Specify a user-specific startup file to invoke if the application + * is run interactively. Typically the startup file is "~/.apprc" + * where "app" is the name of the application. If this line is deleted + * then no user-specific startup file will be run under any conditions. + */ + Tcl_SetVar(interp, "tcl_rcFileName", "~/.wishrc", TCL_GLOBAL_ONLY); + return TCL_OK; + +} + +#endif /* HAS_GUI */ + + +/************************************************************** + * Part 4: Code for implementing pipelined processor simulators + *************************************************************/ + +/****************************************************************************** + * defines + ******************************************************************************/ + +#define MAX_STAGE 10 + +/****************************************************************************** + * static variables + ******************************************************************************/ + +static pipe_ptr pipes[MAX_STAGE]; +static int pipe_count = 0; + +/****************************************************************************** + * function definitions + ******************************************************************************/ + +/* Create new pipe with count bytes of state */ +/* bubble_val indicates state corresponding to pipeline bubble */ +pipe_ptr new_pipe(int count, void *bubble_val) +{ + pipe_ptr result = (pipe_ptr) malloc(sizeof(pipe_ele)); + result->current = malloc(count); + result->next = malloc(count); + memcpy(result->current, bubble_val, count); + memcpy(result->next, bubble_val, count); + result->count = count; + result->op = P_LOAD; + result->bubble_val = bubble_val; + pipes[pipe_count++] = result; + return result; +} + +/* Update all pipes */ +void update_pipes() +{ + int s; + for (s = 0; s < pipe_count; s++) { + pipe_ptr p = pipes[s]; + switch (p->op) + { + case P_BUBBLE: + /* insert a bubble into the next stage */ + memcpy(p->current, p->bubble_val, p->count); + break; + + case P_LOAD: + /* copy calculated state from previous stage */ + memcpy(p->current, p->next, p->count); + break; + case P_ERROR: + /* Like a bubble, but insert error condition */ + memcpy(p->current, p->bubble_val, p->count); + break; + case P_STALL: + default: + /* do nothing: next stage gets same instr again */ + ; + } + if (p->op != P_ERROR) + p->op = P_LOAD; + } +} + +/* Set all pipes to bubble values */ +void clear_pipes() +{ + int s; + for (s = 0; s < pipe_count; s++) { + pipe_ptr p = pipes[s]; + memcpy(p->current, p->bubble_val, p->count); + memcpy(p->next, p->bubble_val, p->count); + p->op = P_LOAD; + } +} + +/******************** Utility Code *************************/ + +/* Representations of digits */ +static char digits[16] = + {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + +/* Print hex/oct/binary format with leading zeros */ +/* bpd denotes bits per digit Should be in range 1-4, + pbw denotes bits per word.*/ +void wprint(uword_t x, int bpd, int bpw, FILE *fp) +{ + int digit; + uword_t mask = ((uword_t) 1 << bpd) - 1; + for (digit = (bpw-1)/bpd; digit >= 0; digit--) { + uword_t val = (x >> (digit * bpd)) & mask; + putc(digits[val], fp); + } +} + +/* Create string in hex/oct/binary format with leading zeros */ +/* bpd denotes bits per digit Should be in range 1-4, + pbw denotes bits per word.*/ +void wstring(uword_t x, int bpd, int bpw, char *str) +{ + int digit; + uword_t mask = ((uword_t) 1 << bpd) - 1; + for (digit = (bpw-1)/bpd; digit >= 0; digit--) { + uword_t val = (x >> (digit * bpd)) & mask; + *str++ = digits[val]; + } + *str = '\0'; +} + + +/******************************** + * Part 5: Stage implementations + *********************************/ + +/*************** Bubbled version of stages *************/ + +pc_ele bubble_pc = {0,STAT_AOK}; +if_id_ele bubble_if_id = { I_NOP, 0, REG_NONE,REG_NONE, + 0, 0, STAT_BUB, 0}; +id_ex_ele bubble_id_ex = { I_NOP, 0, 0, 0, 0, + REG_NONE, REG_NONE, REG_NONE, REG_NONE, + STAT_BUB, 0}; + +ex_mem_ele bubble_ex_mem = { I_NOP, 0, FALSE, 0, 0, + REG_NONE, REG_NONE, STAT_BUB, 0}; + +mem_wb_ele bubble_mem_wb = { I_NOP, 0, 0, 0, REG_NONE, REG_NONE, + STAT_BUB, 0}; + +/*************** Stage Implementations *****************/ + +word_t gen_f_pc(); +word_t gen_need_regids(); +word_t gen_need_valC(); +word_t gen_instr_valid(); +word_t gen_f_predPC(); +word_t gen_f_icode(); +word_t gen_f_ifun(); +word_t gen_f_stat(); +word_t gen_instr_valid(); + +void do_if_stage() +{ + byte_t instr = HPACK(I_NOP, F_NONE); + byte_t regids = HPACK(REG_NONE, REG_NONE); + word_t valc = 0; + word_t valp = f_pc = gen_f_pc(); + + /* Ready to fetch instruction. Speculatively fetch register byte + and immediate word + */ + imem_error = !get_byte_val(mem, valp, &instr); + imem_icode = HI4(instr); + imem_ifun = LO4(instr); + if (!imem_error) { + byte_t junk; + /* Make sure can read maximum length instruction */ + imem_error = !get_byte_val(mem, valp+5, &junk); + } + if_id_next->icode = gen_f_icode(); + if_id_next->ifun = gen_f_ifun(); + if (!imem_error) { + sim_log("\tFetch: f_pc = 0x%llx, imem_instr = %s, f_instr = %s\n", + f_pc, iname(instr), + iname(HPACK(if_id_next->icode, if_id_next->ifun))); + } + + instr_valid = gen_instr_valid(); + if (!instr_valid) + sim_log("\tFetch: Instruction code 0x%llx invalid\n", instr); + if_id_next->status = gen_f_stat(); + + valp++; + if (gen_need_regids()) { + get_byte_val(mem, valp, ®ids); + valp ++; + } + if_id_next->ra = HI4(regids); + if_id_next->rb = LO4(regids); + if (gen_need_valC()) { + get_word_val(mem, valp, &valc); + valp+= 8; + } + if_id_next->valp = valp; + if_id_next->valc = valc; + + pc_next->pc = gen_f_predPC(); + + pc_next->status = (if_id_next->status == STAT_AOK) ? STAT_AOK : STAT_BUB; + + if_id_next->stage_pc = f_pc; +} + +word_t gen_d_srcA(); +word_t gen_d_srcB(); +word_t gen_d_dstE(); +word_t gen_d_dstM(); +word_t gen_d_valA(); +word_t gen_d_valB(); +word_t gen_w_dstE(); +word_t gen_w_valE(); +word_t gen_w_dstM(); +word_t gen_w_valM(); +word_t gen_Stat(); + +/* Implements both ID and WB */ +void do_id_wb_stages() +{ + /* Set up write backs. Don't occur until end of cycle */ + wb_destE = gen_w_dstE(); + wb_valE = gen_w_valE(); + wb_destM = gen_w_dstM(); + wb_valM = gen_w_valM(); + + /* Update processor status */ + status = gen_Stat(); + + id_ex_next->srca = gen_d_srcA(); + id_ex_next->srcb = gen_d_srcB(); + id_ex_next->deste = gen_d_dstE(); + id_ex_next->destm = gen_d_dstM(); + + /* Read the registers */ + d_regvala = get_reg_val(reg, id_ex_next->srca); + d_regvalb = get_reg_val(reg, id_ex_next->srcb); + + /* Do forwarding and valA selection */ + id_ex_next->vala = gen_d_valA(); + id_ex_next->valb = gen_d_valB(); + + id_ex_next->icode = if_id_curr->icode; + id_ex_next->ifun = if_id_curr->ifun; + id_ex_next->valc = if_id_curr->valc; + id_ex_next->stage_pc = if_id_curr->stage_pc; + id_ex_next->status = if_id_curr->status; +} + +word_t gen_alufun(); +word_t gen_set_cc(); +word_t gen_Bch(); +word_t gen_aluA(); +word_t gen_aluB(); +word_t gen_e_valA(); +word_t gen_e_dstE(); + +void do_ex_stage() +{ + alu_t alufun = gen_alufun(); + bool_t setcc = gen_set_cc(); + word_t alua, alub; + + alua = gen_aluA(); + alub = gen_aluB(); + + e_bcond = cond_holds(cc, id_ex_curr->ifun); + + ex_mem_next->takebranch = e_bcond; + + if (id_ex_curr->icode == I_JMP) + sim_log("\tExecute: instr = %s, cc = %s, branch %staken\n", + iname(HPACK(id_ex_curr->icode, id_ex_curr->ifun)), + cc_name(cc), + ex_mem_next->takebranch ? "" : "not "); + + /* Perform the ALU operation */ + word_t aluout = compute_alu(alufun, alua, alub); + ex_mem_next->vale = aluout; + sim_log("\tExecute: ALU: %c 0x%llx 0x%llx --> 0x%llx\n", + op_name(alufun), alua, alub, aluout); + + if (setcc) { + cc_in = compute_cc(alufun, alua, alub); + sim_log("\tExecute: New cc = %s\n", cc_name(cc_in)); + } + + ex_mem_next->icode = id_ex_curr->icode; + ex_mem_next->ifun = id_ex_curr->ifun; + ex_mem_next->vala = gen_e_valA(); + ex_mem_next->deste = gen_e_dstE(); + ex_mem_next->destm = id_ex_curr->destm; + ex_mem_next->srca = id_ex_curr->srca; + ex_mem_next->status = id_ex_curr->status; + ex_mem_next->stage_pc = id_ex_curr->stage_pc; +} + +/* Functions defined using HCL */ +word_t gen_mem_addr(); +word_t gen_mem_read(); +word_t gen_mem_write(); +word_t gen_m_stat(); + +void do_mem_stage() +{ + bool_t read = gen_mem_read(); + + word_t valm = 0; + + mem_addr = gen_mem_addr(); + mem_data = ex_mem_curr->vala; + mem_write = gen_mem_write(); + dmem_error = FALSE; + + if (read) { + dmem_error = dmem_error || !get_word_val(mem, mem_addr, &valm); + if (!dmem_error) + sim_log("\tMemory: Read 0x%llx from 0x%llx\n", + valm, mem_addr); + } + if (mem_write) { + word_t sink; + /* Do a read of address just to check validity */ + dmem_error = dmem_error || !get_word_val(mem, mem_addr, &sink); + if (dmem_error) + sim_log("\tMemory: Invalid address 0x%llx\n", + mem_addr); + } + mem_wb_next->icode = ex_mem_curr->icode; + mem_wb_next->ifun = ex_mem_curr->ifun; + mem_wb_next->vale = ex_mem_curr->vale; + mem_wb_next->valm = valm; + mem_wb_next->deste = ex_mem_curr->deste; + mem_wb_next->destm = ex_mem_curr->destm; + mem_wb_next->status = gen_m_stat(); + mem_wb_next->stage_pc = ex_mem_curr->stage_pc; +} + +/* Set stalling conditions for different stages */ + +word_t gen_F_stall(), gen_F_bubble(); +word_t gen_D_stall(), gen_D_bubble(); +word_t gen_E_stall(), gen_E_bubble(); +word_t gen_M_stall(), gen_M_bubble(); +word_t gen_W_stall(), gen_W_bubble(); + +p_stat_t pipe_cntl(char *name, word_t stall, word_t bubble) +{ + if (stall) { + if (bubble) { + sim_log("%s: Conflicting control signals for pipe register\n", + name); + return P_ERROR; + } else + return P_STALL; + } else { + return bubble ? P_BUBBLE : P_LOAD; + } +} + +void do_stall_check() +{ + pc_state->op = pipe_cntl("PC", gen_F_stall(), gen_F_bubble()); + if_id_state->op = pipe_cntl("ID", gen_D_stall(), gen_D_bubble()); + id_ex_state->op = pipe_cntl("EX", gen_E_stall(), gen_E_bubble()); + ex_mem_state->op = pipe_cntl("MEM", gen_M_stall(), gen_M_bubble()); + mem_wb_state->op = pipe_cntl("WB", gen_W_stall(), gen_W_bubble()); +} + + + diff --git a/arch/sim/pipe/sdriver.ys b/arch/sim/pipe/sdriver.ys new file mode 100644 index 0000000..929bc2c --- /dev/null +++ b/arch/sim/pipe/sdriver.ys @@ -0,0 +1,145 @@ +####################################################################### +# Test for copying block of size 4; +####################################################################### + .pos 0 +main: irmovq Stack, %rsp # Set up stack pointer + + # Set up arguments for copy function and then invoke it + irmovq $4, %rdx # src and dst have 4 elements + irmovq dest, %rsi # dst array + irmovq src, %rdi # src array + call ncopy + halt # should halt with num nonzeros in %rax +StartFun: +#/* $begin ncopy-ys */ +################################################################## +# ncopy.ys - Copy a src block of len words to dst. +# Return the number of positive words (>0) contained in src. +# +# Include your name and ID here. +# +# Describe how and why you modified the baseline code. +# +################################################################## +# Do not modify this portion +# Function prologue. +# %rdi = src, %rsi = dst, %rdx = len +ncopy: + +################################################################## +# You can modify this portion + # Loop header + xorq %rax,%rax # count = 0; + iaddq $0xfffffffffffffffc, %rdx + jle Tail # if so, goto Done: + +Loop1: + mrmovq (%rdi), %r10 # read val from src... + rmmovq %r10, (%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos1 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos1: + + mrmovq 8(%rdi), %r10 # read val from src... + rmmovq %r10, 8(%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos2 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos2: + + mrmovq 16(%rdi), %r10 # read val from src... + rmmovq %r10, 16(%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos3 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos3: + + mrmovq 24(%rdi), %r10 # read val from src... + rmmovq %r10, 24(%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos4 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos4: + + mrmovq 32(%rdi), %r10 # read val from src... + rmmovq %r10, 32(%rsi) # ...and store it to dst + andq %r10, %r10 # val <= 0? + jle Npos5 # if so, goto Npos: + iaddq $1, %rax # count++ +Npos5: + + iaddq $40, %rdi # src++ + iaddq $40, %rsi # dst++ + iaddq $0xfffffffffffffffb, %rdx # len-- + jg Loop1 + +Tail: + iaddq $4, %rdx + jle Done +Loop2: + mrmovq (%rdi), %r10 + rmmovq %r10, (%rsi) + andq %r10, %r10 + jle NposT + iaddq $1, %rax +NposT: + iaddq $8, %rdi + iaddq $8, %rsi + iaddq $0xffffffffffffffff, %rdx + jg Loop2 + + +################################################################## +# Do not modify the following section of code +# Function epilogue. +Done: + ret +################################################################## +# Keep the following label at the end of your function +End: +#/* $end ncopy-ys */ +EndFun: + +############################### +# Source and destination blocks +############################### + .align 8 +src: + .quad 1 + .quad -2 + .quad 3 + .quad -4 + .quad 0xbcdefa # This shouldn't get moved + + .align 16 +Predest: + .quad 0xbcdefa +dest: + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab + .quad 0xcdefab +Postdest: + .quad 0xdefabc + +.align 8 +# Run time stack + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + .quad 0 + +Stack: diff --git a/arch/sim/pipe/sim.h b/arch/sim/pipe/sim.h new file mode 100644 index 0000000..b176318 --- /dev/null +++ b/arch/sim/pipe/sim.h @@ -0,0 +1,153 @@ + +/********** Typedefs ************/ + +/* EX stage mux settings */ +typedef enum { MUX_NONE, MUX_EX_A, MUX_EX_B, MUX_MEM_E, + MUX_WB_M, MUX_WB_E } mux_source_t; + +/* Simulator operating modes */ +typedef enum { S_WEDGED, S_STALL, S_FORWARD } sim_mode_t; + +/* Pipeline stage identifiers for stage operation control */ +typedef enum { IF_STAGE, ID_STAGE, EX_STAGE, MEM_STAGE, WB_STAGE } stage_id_t; + +/********** Defines **************/ + +/* Get ra out of one byte regid field */ +#define GET_RA(r) HI4(r) + +/* Get rb out of one byte regid field */ +#define GET_RB(r) LO4(r) + + +/************ Global state declaration ****************/ + +/* How many cycles have been simulated? */ +extern word_t cycles; +/* How many instructions have passed through the EX stage? */ +extern word_t instructions; + +/* Both instruction and data memory */ +extern mem_t mem; + +/* Keep track of range of addresses that have been written */ +extern word_t minAddr; +extern word_t memCnt; + +/* Register file */ +extern mem_t reg; +/* Condition code register */ +extern cc_t cc; +extern stat_t stat; + +/* Operand sources in EX (to show forwarding) */ +extern mux_source_t amux, bmux; + +/* Provide global access to current states of all pipeline registers */ +pipe_ptr pc_state, if_id_state, id_ex_state, ex_mem_state, mem_wb_state; + +/* Current States */ +extern pc_ptr pc_curr; +extern if_id_ptr if_id_curr; +extern id_ex_ptr id_ex_curr; +extern ex_mem_ptr ex_mem_curr; +extern mem_wb_ptr mem_wb_curr; + +/* Next States */ +extern pc_ptr pc_next; +extern if_id_ptr if_id_next; +extern id_ex_ptr id_ex_next; +extern ex_mem_ptr ex_mem_next; +extern mem_wb_ptr mem_wb_next; + +/* Pending updates to state */ +extern word_t cc_in; +extern word_t wb_destE; +extern word_t wb_valE; +extern word_t wb_destM; +extern word_t wb_valM; +extern word_t mem_addr; +extern word_t mem_data; +extern bool_t mem_write; + + +/* Intermdiate stage values that must be used by control functions */ +extern word_t f_pc; +extern byte_t imem_icode; +extern byte_t imem_ifun; +extern bool_t imem_error; +extern bool_t instr_valid; +extern word_t d_regvala; +extern word_t d_regvalb; +extern word_t e_vala; +extern word_t e_valb; +extern bool_t e_bcond; +extern bool_t dmem_error; + +/* Simulator operating mode */ +extern sim_mode_t sim_mode; +/* Log file */ +extern FILE *dumpfile; + +/*************** Simulation Control Functions ***********/ + +/* Bubble next execution of specified stage */ +void sim_bubble_stage(stage_id_t stage); + +/* Stall stage (has effect at next update) */ +void sim_stall_stage(stage_id_t stage); + +/* Sets the simulator name (called from main routine in HCL file) */ +void set_simname(char *name); + +/* Initialize simulator */ +void sim_init(); + +/* Reset simulator state, including register, instruction, and data memories */ +void sim_reset(); + +/* + Run pipeline until one of following occurs: + - A status error is encountered in WB. + - max_instr instructions have completed through WB + - max_cycle cycles have been simulated + + Return number of instructions executed. + if statusp nonnull, then will be set to status of final instruction + if ccp nonnull, then will be set to condition codes of final instruction +*/ +word_t sim_run_pipe(word_t max_instr, word_t max_cycle, byte_t *statusp, cc_t *ccp); + +/* If dumpfile set nonNULL, lots of status info printed out */ +void sim_set_dumpfile(FILE *file); + +/* + * sim_log dumps a formatted string to the dumpfile, if it exists + * accepts variable argument list + */ +void sim_log( const char *format, ... ); + + +/******************* GUI Interface Functions **********************/ +#ifdef HAS_GUI + +void signal_sources(); + +void signal_register_clear(); + +void report_pc(unsigned fpc, unsigned char fpcv, + unsigned dpc, unsigned char dpcv, + unsigned epc, unsigned char epcv, + unsigned mpc, unsigned char mpcv, + unsigned wpc, unsigned char wpcv); + +void report_state(char *id, word_t current, char *txt); + +void show_cc(cc_t cc); +void show_cpi(); +void show_stat(stat_t stat); + +void create_memory_display(); +void set_memory(word_t addr, word_t val); +#endif + diff --git a/arch/sim/pipe/stages.h b/arch/sim/pipe/stages.h new file mode 100644 index 0000000..a86db9a --- /dev/null +++ b/arch/sim/pipe/stages.h @@ -0,0 +1,90 @@ +/* + * stages.h - Defines the layout of the pipe registers + * Declares the functions that implement the pipeline stages +*/ + +/********** Pipeline register contents **************/ + +/* Program Counter */ +typedef struct { + word_t pc; + stat_t status; +} pc_ele, *pc_ptr; + +/* IF/ID Pipe Register */ +typedef struct { + byte_t icode; /* Single byte instruction code */ + byte_t ifun; /* ALU/JMP qualifier */ + byte_t ra; /* Register ra ID */ + byte_t rb; /* Register rb ID */ + word_t valc; /* Instruction word encoding immediate data */ + word_t valp; /* Incremented program counter */ + stat_t status; + /* The following is included for debugging */ + word_t stage_pc; +} if_id_ele, *if_id_ptr; + +/* ID/EX Pipe Register */ +typedef struct { + byte_t icode; /* Instruction code */ + byte_t ifun; /* ALU/JMP qualifier */ + word_t valc; /* Immediate data */ + word_t vala; /* valA */ + word_t valb; /* valB */ + byte_t srca; /* Source Reg ID for valA */ + byte_t srcb; /* Source Reg ID for valB */ + byte_t deste; /* Destination register for valE */ + byte_t destm; /* Destination register for valM */ + stat_t status; + /* The following is included for debugging */ + word_t stage_pc; +} id_ex_ele, *id_ex_ptr; + +/* EX/MEM Pipe Register */ +typedef struct { + byte_t icode; /* Instruction code */ + byte_t ifun; /* ALU/JMP qualifier */ + bool_t takebranch; /* Taken branch signal */ + word_t vale; /* valE */ + word_t vala; /* valA */ + byte_t deste; /* Destination register for valE */ + byte_t destm; /* Destination register for valM */ + byte_t srca; /* Source register for valA */ + stat_t status; + /* The following is included for debugging */ + word_t stage_pc; +} ex_mem_ele, *ex_mem_ptr; + +/* Mem/WB Pipe Register */ +typedef struct { + byte_t icode; /* Instruction code */ + byte_t ifun; /* ALU/JMP qualifier */ + word_t vale; /* valE */ + word_t valm; /* valM */ + byte_t deste; /* Destination register for valE */ + byte_t destm; /* Destination register for valM */ + stat_t status; + /* The following is included for debugging */ + word_t stage_pc; +} mem_wb_ele, *mem_wb_ptr; + +/************ Global Declarations ********************/ + +extern pc_ele bubble_pc; +extern if_id_ele bubble_if_id; +extern id_ex_ele bubble_id_ex; +extern ex_mem_ele bubble_ex_mem; +extern mem_wb_ele bubble_mem_wb; + +/************ Function declarations *******************/ + +/* Stage functions */ +void do_if_stage(); +void do_id_wb_stages(); /* Both ID and WB */ +void do_ex_stage(); +void do_mem_stage(); + +/* Set stalling conditions for different stages */ +void do_stall_check(); + + diff --git a/arch/sim/ptest/Makefile b/arch/sim/ptest/Makefile new file mode 100644 index 0000000..bc356e2 --- /dev/null +++ b/arch/sim/ptest/Makefile @@ -0,0 +1,20 @@ +SIM=../pipe/psim +TFLAGS= + +ISADIR = ../misc +YAS=$(ISADIR)/yas + +.SUFFIXES: .ys .yo + +.ys.yo: + $(YAS) $*.ys + +test: + ./optest.pl -s $(SIM) $(TFLAGS) + ./jtest.pl -s $(SIM) $(TFLAGS) + ./ctest.pl -s $(SIM) $(TFLAGS) + ./htest.pl -s $(SIM) $(TFLAGS) + +clean: + rm -f *.o *~ *.yo *.ys + diff --git a/arch/sim/ptest/README b/arch/sim/ptest/README new file mode 100644 index 0000000..7ea9625 --- /dev/null +++ b/arch/sim/ptest/README @@ -0,0 +1,36 @@ +This directory contains Perl scripts that provide comprehensive +regression testing of the different Y86-64 simulators. There are four +basic test types, implemented as four different scripts: + + optest.pl: Tests each individual instruction type + jtest.pl: Tests all of the jump types under different conditions + ctest.pl: Tests different pipeline control combinations + htest.pl: Tests many different hazard possibilities + This involves running 864+ tests, so it takes a while. + +Each of the tests has the following optional arguments: + -s simfile Use simfile as simulator (default ../pipe/psim). + -i Test the iaddq instruction + +You can use make to run all four test programs. Options to make include: + + SIM=simfile + TFLAGS= + +For example, you could say: + make SIM=../pipe/psim TFLAGS=-i +to test the pipeline simulator including the iaddq instruction. (Note that +this test will fail for the default implementation of pipe, since it does +not implement the iaddq instruction.) + +When the test program detects an erroneous simulation, it leaves the +.ys file in the directory (ordinarily it deletes the test code it +generates). You can then run a simulator (the GUI version is +especially helpful here) on one of these failing cases. Suppose the +failing test is in file bad-test.ys. Then you can execute "make +bad-test.yo" to create the object code, and simulate it with one of +the simulators. + +Note that the standard test code only detects functional bugs, where the +processor simulation produces different results than would be +predicted by simulating at the ISA level. diff --git a/arch/sim/ptest/ctest.pl b/arch/sim/ptest/ctest.pl new file mode 100755 index 0000000..1788aee --- /dev/null +++ b/arch/sim/ptest/ctest.pl @@ -0,0 +1,113 @@ +#!/usr/bin/perl +#!/usr/local/bin/perl +# Test for pipeline hazard combinations + +use Getopt::Std; +use lib "."; +use tester; + +cmdline(); + +# Instruction templates +$tcount = 8; + +@templates = +( + "||jne target\n\thalt\ntarget:|", # M + "|||ret", # R + + "||mrmovq (%rax),%rsp|ret", # G1a + "|mrmovq (%rax),%rsp||ret", # G1b + "mrmovq (%rax),%rsp|||ret", # G1c + "||irmovq \$3,%rax|rrmovq %rax,%rdx", # G2a + "|irmovq \$3,%rax||rrmovq %rax,%rdx", # G2b + "irmovq \$3,%rax|||rrmovq %rax,%rdx", # G2c +); + +# Try combining two templates to generate test sequence +sub make_test +{ + local ($t1, $t2) = @_; + $ok = 1; + @test1 = split(/\|/, $t1); + @test2 = split(/\|/, $t2); + for ($i = 0; $i < 4; $i++) { + if ($test1[$i] eq "") { + if ($test2[$i] eq "") { + $test[$i] = "nop"; + } else { + $test[$i] = $test2[$i]; + } + } else { + if ($test2[$i] eq "") { + $test[$i] = $test1[$i]; + } else { + if ($test1[$i] eq $test2[$i]) { +# $ok = 0; + $test[$i] = $test1[$i]; + } else { + $ok = 0; + $test[$i] = "XXX"; + } + } + } + } + if ($ok) { + &gen_test($test[0], $test[1], $test[2], $test[3]); + } + +} + +$testcnt = 0; + +# Generate test with 4 instructions inserted +sub gen_test +{ + local ($i1, $i2, $i3, $i4) = @_; + $tname = "c-$testcnt"; + $testcnt++; + open(YFILE, ">$tname.ys") || die "Can't write to $tname.ys\n"; + + print YFILE <$tname.ys") || die "Can't write to $tname.ys\n"; + &gen_test($e, "nop", $s); + close YFILE; + &run_test($tname); + + # Two instructions in succession + $tname = "e-$ei-$si"; + open (YFILE, ">$tname.ys") || die "Can't write to $tname.ys\n"; + &gen_test($e, "", $s); + close YFILE; + &run_test($tname); + $si++; + } + $si = 0; + $ei++; +} + +&test_stat(); diff --git a/arch/sim/ptest/htest.pl b/arch/sim/ptest/htest.pl new file mode 100755 index 0000000..210f940 --- /dev/null +++ b/arch/sim/ptest/htest.pl @@ -0,0 +1,249 @@ +#!/usr/bin/perl +#!/usr/local/bin/perl +# Test for pipeline hazards + +use Getopt::Std; +use lib "."; +use tester; + +cmdline(); + +# Destination Instructions +@dest = +( + # Having %rax as destination + "1:rrmovq %rcx,%rax", + "1:irmovq \$0x101,%rax", + "1:mrmovq 0(%rbp),%rax", + "1:addq %rax,%rax", + "1:popq %rax", + "1:cmovne %rcx,%rax", # Not taken + "1:cmove %rcx,%rax", # Taken + # Instructions having %rbp as destination + "2:rrmovq %rax,%rbp", + "2:irmovq \$0x100,%rbp", + "2:mrmovq 4(%rbp),%rbp", + "2:addq %rax,%rbp", + "2:popq %rbp", + "2:cmovne %rax,%rbp", # Not taken + "2:cmove %rax,%rbp", # Taken + # Instructions having %rsp as destination + "3:rrmovq %rbp,%rsp", + "3:irmovq \$0x104,%rsp", + "3:mrmovq 4(%rbp),%rsp", + "3:addq %rax,%rsp", + "3:popq %rbp", + "3:pushq %rax", + "3:pushq %rsp", + "3:popq %rsp", + "1:cmovne %rbp,%rsp", # Not taken + "1:cmove %rbp,%rsp" # Taken + ); + +if ($testiaddq) { + @dest = (@dest, + "1:iaddq \$0x201,%rax", + "2:iaddq \$0x4,%rbp", + "3:iaddq \$0x4,%rsp",); +} + +if ($testleave) { + @dest = (@dest, "2:leave", "3:leave"); +} + +@src = +( + # Instructions having %rax as source + "1:rrmovq %rax,%rbp", + "1:rmmovq %rax,0(%rbp)", + "1:rmmovq %rbp,0(%rax)", + "1:mrmovq 4(%rax),%rbp", + "1:addq %rax,%rbp", + "1:addq %rbp,%rax", + "1:addq %rax,%rax", + "1:pushq %rax", + # Instructions having %rbp as source + "2:rrmovq %rbp,%rbp", + "2:rmmovq %rbp,4(%rbp)", + "2:rmmovq %rax,0(%rbp)", + "2:mrmovq 8(%rbp),%rax", + "2:addq %rbp,%rax", + "2:addq %rax,%rbp", + "2:addq %rbp,%rbp", + "2:pushq %rbp", + # Instructions having %rsp as source + "3:rrmovq %rsp,%rbp", + "3:rmmovq %rsp,4(%rbp)", + "3:rmmovq %rax,-4(%rsp)", + "3:mrmovq 4(%rsp),%rax", + "3:addq %rsp,%rax", + "3:addq %rax,%rsp", + "3:addq %rsp,%rsp", + "3:pushq %rsp", + "3:ret" + ); + +if ($testiaddq) { + @src = (@src, + "1:iaddq \$0x301,%rax", + "2:iaddq \$0x8,%rbp", + "3:iaddq \$0x8,%rsp"); +} + +# Generate test with 4 instructions inserted +sub gen_test +{ + local ($i1, $i2, $i3, $i4) = @_; + print YFILE <$tname.ys") || die "Can't write to $tname.ys\n"; + &gen_test($d, "nop", "nop", $s); + close YFILE; + &run_test($tname); + + # Two instructions with nop between them + $tname = "hn-$di-$si"; + open (YFILE, ">$tname.ys") || die "Can't write to $tname.ys\n"; + &gen_test($d, "nop", "", $s); + close YFILE; + &run_test($tname); + + # Two instructions in succession + $tname = "h-$di-$si"; + open (YFILE, ">$tname.ys") || die "Can't write to $tname.ys\n"; + &gen_test($d, "", "", $s); + close YFILE; + &run_test($tname); + } + $si++; + } + $si = 0; + $di++; +} + +&test_stat(); diff --git a/arch/sim/ptest/jtest.pl b/arch/sim/ptest/jtest.pl new file mode 100755 index 0000000..5d60b5f --- /dev/null +++ b/arch/sim/ptest/jtest.pl @@ -0,0 +1,127 @@ +#!/usr/bin/perl +#!/usr/local/bin/perl +# Test jump instructions + +use Getopt::Std; +use lib "."; +use tester; + +cmdline(); + +@vals = (32, 64); + +@instr = ("jmp", "jle", "jl", "je", "jne", "jge", "jg", "call"); + +# Create set of forward tests +foreach $t (@instr) { + foreach $va (@vals) { + foreach $vb (@vals) { + $tname = "jf-$t-$va-$vb"; + open (YFILE, ">$tname.ys") || die "Can't write to $tname.ys\n"; + print YFILE <$tname.ys") || die "Can't write to $tname.ys\n"; + print YFILE <$tname.ys") || die "Can't write to $tname.ys\n"; + print YFILE <$tname.ys") || die "Can't write to $tname.ys\n"; + print YFILE <$tname.ys") || die "Can't write to $tname.ys\n"; + print YFILE <$tname.ys") || die "Can't write to $tname.ys\n"; + print YFILE <] [-P] [-p ]\n"; + print STDERR " -h print Help message\n"; + print STDERR " -i test iaddq instruction\n"; + print STDERR " -s Specify simulator\n"; + print STDERR " -d Specify directory for counterexamples\n"; + print STDERR " -P Generate performance data\n"; + print STDERR " -p Check using performance file \n"; + print STDERR " -V test Verilog implementation\n"; + print STDERR " -m Model for Verilog\n"; + die "\n"; + } + + if ($opt_i) { + $testiaddq = 1; + } + + if ($opt_d) { + $outputdir = $opt_d; + } + + if ($opt_P) { + $gen_perf = 1; + } + + if ($opt_p) { + $check_perf = 1; + $perf_file = $opt_p; + } + + if ($opt_s) { + $sim = $opt_s; + } + if ($opt_V) { + $test_vlog = 1; + if ($opt_m) { + $vmodel = $opt_m; + } + } else { + print "Simulating with $sim\n"; + } +} + +# Perl gives error messages without the following line !?! +$junk = 1; + + + + + diff --git a/arch/sim/seq/Makefile b/arch/sim/seq/Makefile new file mode 100644 index 0000000..0c71aae --- /dev/null +++ b/arch/sim/seq/Makefile @@ -0,0 +1,65 @@ +# Modify this line to indicate the default version + +VERSION=std + +# Comment this out if you don't have Tcl/Tk on your system + +GUIMODE=-DHAS_GUI + +# Modify the following line so that gcc can find the libtcl.so and +# libtk.so libraries on your system. You may need to use the -L option +# to tell gcc which directory to look in. Comment this out if you +# don't have Tcl/Tk. + +TKLIBS=-L/usr/lib -ltk -ltcl + +# Modify the following line so that gcc can find the tcl.h and tk.h +# header files on your system. Comment this out if you don't have +# Tcl/Tk. + +TKINC=-isystem /usr/include/tcl8.5 + +# Modify these two lines to choose your compiler and compile time +# flags. + +CC=gcc +CFLAGS=-Wall -O2 + +################################################## +# You shouldn't need to modify anything below here +################################################## + +MISCDIR=../misc +HCL2C=$(MISCDIR)/hcl2c +INC=$(TKINC) -I$(MISCDIR) $(GUIMODE) +LIBS=$(TKLIBS) -lm +YAS=../misc/yas + +all: ssim + +# This rule builds the SEQ simulator (ssim) +ssim: seq-$(VERSION).hcl ssim.c sim.h $(MISCDIR)/isa.c $(MISCDIR)/isa.h + # Building the seq-$(VERSION).hcl version of SEQ + $(HCL2C) -n seq-$(VERSION).hcl seq-$(VERSION).c + $(CC) $(CFLAGS) $(INC) -o ssim \ + seq-$(VERSION).c ssim.c $(MISCDIR)/isa.c $(LIBS) + +# This rule builds the SEQ+ simulator (ssim+) +ssim+: seq+-std.hcl ssim.c sim.h $(MISCDIR)/isa.c $(MISCDIR)/isa.h + # Building the seq+-std.hcl version of SEQ+ + $(HCL2C) -n seq+-std.hcl seq+-std.c + $(CC) $(CFLAGS) $(INC) -o ssim+ \ + seq+-std.c ssim.c $(MISCDIR)/isa.c $(LIBS) + +# These are implicit rules for assembling .yo files from .ys files. +.SUFFIXES: .ys .yo +.ys.yo: + $(YAS) $*.ys + + +clean: + rm -f ssim ssim+ seq*-*.c *.o *~ *.exe *.yo *.ys + + + + diff --git a/arch/sim/seq/README b/arch/sim/seq/README new file mode 100644 index 0000000..2a3157f --- /dev/null +++ b/arch/sim/seq/README @@ -0,0 +1,91 @@ +/*********************************************************************** + * Sequential Y86-64 Simulators + * + * Copyright (c) 2002, 2010, 2013, 2015 R. Bryant and D. O'Hallaron, + * All rights reserved. + * May not be used, modified, or copied without permission. + ***********************************************************************/ + +This directory contains the code to construct simulators for SEQ, +SEQ+, and the variants of it described in the homework exercises. + +************************** +1. Building the simulators +************************** + +Different versions of the SEQ and SEQ+ simulators can be constructed +to use different HCL files when working on the different homework +problems. + +Binary VERSION HCL File Description +ssim std seq-std.hcl Standard SEQ simulator described in textbook. +ssim full seq-full.hcl For adding iaddq to SEQ. +ssim+ std seq+-std.hcl Standard SEQ+ simulator described in textbook. + +The simulators run in either TTY or GUI mode: + +o TTY mode: A simulator running in TTY mode prints all information +about its runtime behavior on the terminal. It's hard to understand what's +going on, but useful for automated testing, and doesn't require any +special installation features. + +o GUI mode: A simulator running in GUI mode uses a fancy graphical +user interface. Nice for visualizing and debugging, but requires +installation of Tcl/Tk on your system. + +The Makefile has simple instructions for building TTY or GUI +simulators. A TTY simulator runs in TTY mode only. A GUI +simulator can run in either TTY mode or GUI mode, according to +a command line argument. + +Once you've configured the Makefile, you can build the different +simulators with commands of the form + + unix> make clean; make ssim VERSION=xxx + +where "xxx" is one of the versions listed above. For example, to build +the version of SEQ described in the CS:APP text based on the control +logic in seq-std.hcl, type + + unix> make clean; make ssim VERSION=std + +To save typing, you can also set the Makefile's VERSION variable. + +*********************** +2. Using the simulators +*********************** + +The simulators take identical command line arguments: + +Usage: ssim [-htg] [-l m] [-v n] file.yo + +file.yo required in GUI mode, optional in TTY mode (default stdin) + + -h Print this message + -g Run in GUI mode instead of TTY mode (default TTY mode) + -l m Set instruction limit to m [TTY mode only] (default 10000) + -v n Set verbosity level to 0 <= n <= 2 [TTY mode only] (default 2) + -t Test result against the ISA simulator (yis) [TTY model only] + +******** +3. Files +******** + +Makefile Builds the SEQ and SEQ+ simulators +Makefile-sim Makefile for student distribution +README This file + +seq+.tcl TCL script for GUI version of SEQ+ +seq.tcl TCL script for GUI version of SEQ + +ssim.c Base sequential simulator code and header file +sim.h + +seq-std.hcl Standard SEQ control logic +seq+-std.hcl Standard SEQ+ control logic +seq-full.hcl Template for the iaddq problem (4.34-35) + +seq-full-ans.hcl Solution for the iaddq problems (4.34-35) + (Instructor distribution only) + + diff --git a/arch/sim/seq/description.md b/arch/sim/seq/description.md new file mode 100644 index 0000000..c42d1b3 --- /dev/null +++ b/arch/sim/seq/description.md @@ -0,0 +1,11 @@ +|阶段|iaddq V,rB| +|:---|:---| +|取指|icode:ifun<-M1[PC]| +||rA:rB<-M1[PC+1]| +||valC<-M8[PC+2]| +||valP<-PC+10| +|译码|valB<-R[rB]| +|执行|valE<-valB+valC| +|访存|| +|写回|R[rB]<-valE| +|更新 PC|PC<-valP| diff --git a/arch/sim/seq/seq+-std.hcl b/arch/sim/seq/seq+-std.hcl new file mode 100644 index 0000000..a045832 --- /dev/null +++ b/arch/sim/seq/seq+-std.hcl @@ -0,0 +1,226 @@ +#/* $begin seq-all-hcl */ +#################################################################### +# HCL Description of Control for Single Cycle Y86-64 Processor SEQ+ # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2010 # +#################################################################### + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'word_t gen_new_pc(){return 0;}' +quote 'int main(int argc, char *argv[])' +quote ' {plusmode=1;return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced explicitly ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ##### +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic #################### + +##### PC stage inputs ##### + +## All of these values are based on those from previous instruction +wordsig pIcode 'prev_icode' # Instr. control code +wordsig pValC 'prev_valc' # Constant from instruction +wordsig pValM 'prev_valm' # Value read from memory +wordsig pValP 'prev_valp' # Incremented program counter +boolsig pCnd 'prev_bcond' # Condition flag + +##### Fetch stage computations ##### +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig icode 'icode' # Instruction control code +wordsig ifun 'ifun' # Instruction function +wordsig rA 'ra' # rA field from instruction +wordsig rB 'rb' # rB field from instruction +wordsig valC 'valc' # Constant from instruction +wordsig valP 'valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Decode stage computations ##### +wordsig valA 'vala' # Value from register A port +wordsig valB 'valb' # Value from register B port + +##### Execute stage computations ##### +wordsig valE 'vale' # Value computed by ALU +boolsig Cnd 'cond' # Branch test + +##### Memory stage computations ##### +wordsig valM 'valm' # Value read from memory +boolsig dmem_error 'dmem_error' # Error signal from data memory + + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Program Counter Computation ####################### + +# Compute fetch location for this instruction based on results from +# previous instruction. + +word pc = [ + # Call. Use instruction constant + pIcode == ICALL : pValC; + # Taken branch. Use instruction constant + pIcode == IJXX && pCnd : pValC; + # Completion of RET instruction. Use value from stack + pIcode == IRET : pValM; + # Default: Use incremented PC + 1 : pValP; +]; +#/* $end seq-plus-pc-hcl */ + +################ Fetch Stage ################################### + +# Determine instruction code +word icode = [ + imem_error: INOP; + 1: imem_icode; # Default: get from instruction memory +]; + +# Determine instruction function +word ifun = [ + imem_error: FNONE; + 1: imem_ifun; # Default: get from instruction memory +]; + +bool instr_valid = icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ }; + +# Does fetched instruction require a regid byte? +bool need_regids = + icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +################ Decode Stage ################################### + +## What register should be used as the A source? +word srcA = [ + icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : rA; + icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word srcB = [ + icode in { IOPQ, IRMMOVQ, IMRMOVQ } : rB; + icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word dstE = [ + icode in { IRRMOVQ } && Cnd : rB; + icode in { IIRMOVQ, IOPQ} : rB; + icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word dstM = [ + icode in { IMRMOVQ, IPOPQ } : rA; + 1 : RNONE; # Don't write any register +]; + +################ Execute Stage ################################### + +## Select input A to ALU +word aluA = [ + icode in { IRRMOVQ, IOPQ } : valA; + icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : valC; + icode in { ICALL, IPUSHQ } : -8; + icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ } : valB; + icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + icode == IOPQ : ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = icode in { IOPQ }; + +################ Memory Stage ################################### + +## Set read control signal +bool mem_read = icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = icode in { IRMMOVQ, IPUSHQ, ICALL }; + +## Select memory address +word mem_addr = [ + icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : valE; + icode in { IPOPQ, IRET } : valA; + # Other instructions don't need address +]; + +## Select memory input data +word mem_data = [ + # Value from register + icode in { IRMMOVQ, IPUSHQ } : valA; + # Return PC + icode == ICALL : valP; + # Default: Don't write anything +]; + +## Determine instruction status +word Stat = [ + imem_error || dmem_error : SADR; + !instr_valid: SINS; + icode == IHALT : SHLT; + 1 : SAOK; +]; +#/* $end seq-all-hcl */ diff --git a/arch/sim/seq/seq+.tcl b/arch/sim/seq/seq+.tcl new file mode 100644 index 0000000..73b0627 --- /dev/null +++ b/arch/sim/seq/seq+.tcl @@ -0,0 +1,540 @@ +########################################################################## +# Parsing of command line flags # +########################################################################## + +proc flagVal {flag default} { + global argv + foreach t $argv { + if {[string match "-$flag*" $t]} {return [string range $t 2 end]} + } + return $default +} + +proc findFlag {flag} { + global argv + foreach t $argv { + if {[string match "-$flag" $t]} {return 1} + } + return 0 +} + +########################################################################## +# Register File Implementation. Shown as array of 8 columns # +########################################################################## + + +# Font used to display register contents +set fontSize [expr 10 * [flagVal "f" 12]] +set codeFontSize [expr 10 * [flagVal "c" 10]] +set labFontSize [expr 10 * [flagVal "l" 10]] +set bigFontSize [expr 10 * [flagVal "b" 16]] +set dpyFont "*-courier-medium-r-normal--*-$fontSize-*-*-*-*-*-*" +set labFont "*-helvetica-medium-r-normal--*-$labFontSize-*-*-*-*-*-*" +set bigLabFont "*-helvetica-bold-r-normal--*-$bigFontSize-*-*-*-*-*-*" +set codeFont "*-courier-medium-r-normal--*-$codeFontSize-*-*-*-*-*-*" +# Background Color of normal register +set normalBg white +# Background Color of highlighted register +set specialBg LightSkyBlue + +# Height of titles separating major sections of control panel +set sectionHeight 2 + + +# How many rows of code do I display +set codeRowCount [flagVal "r" 50] + +# Keep track of previous highlighted register +set lastId -1 +proc setReg {id val highlight} { + global lastId normalBg specialBg + if {$lastId >= 0} { + .r.reg$lastId config -bg $normalBg + set lastId -1 + } + if {$id < 0 || $id >= 15} { + error "Invalid Register ($id)" + } + .r.reg$id config -text [format %16x $val] + if {$highlight} { + uplevel .r.reg$id config -bg $specialBg + set lastId $id + } +} + +# Clear all registers +proc clearReg {} { + global lastId normalBg + if {$lastId >= 0} { + .r.reg$lastId config -bg $normalBg + set lastId -1 + } + for {set i 0} {$i < 8} {incr i 1} { + .r.reg$i config -text "" + } +} + +# Set all 3 condition codes +proc setCC {zv cv ov} { + .cc.cc0 config -text [format %d $zv] + .cc.cc1 config -text [format %d $cv] + .cc.cc2 config -text [format %d $ov] +} + + +### Create display for misc. state +frame .flags +pack .flags -in . -side bottom + +############################################################################## +# Status Display # +############################################################################## + +set simStat "AOK" +# Line to display simulation status +frame .stat +pack .stat -in .flags -side left +label .stat.statlab -width 7 -text "Stat" -height $sectionHeight -font $bigLabFont +label .stat.statdpy -width 3 -font $dpyFont -relief ridge -bg white -textvariable simStat +label .stat.fill -width 6 -text "" +pack .stat.statlab .stat.statdpy .stat.fill -in .stat -side left +############################################################################## +# Condition Code Display # +############################################################################## +# Create Window for condition codes +frame .cc +pack .cc -in .flags -side right + +label .cc.lab -text "Condition Codes" -height $sectionHeight -font $bigLabFont +pack .cc.lab -in .cc -side left + + +set ccnames [list "Z" "S" "O"] + +# Create Row of CC Labels +for {set i 0} {$i < 3} {incr i 1} { + label .cc.lab$i -width 1 -font $dpyFont -text [lindex $ccnames $i] + pack .cc.lab$i -in .cc -side left + label .cc.cc$i -width 1 -font $dpyFont -relief ridge -bg $normalBg + pack .cc.cc$i -in .cc -side left +} + +############################################################################## +# Register Display # +############################################################################## + + +# Create Window for registers +frame .r +pack .r -in . -side bottom +# Following give separate window for register file +# toplevel .r +# wm title .r "Register File" -height $sectionHeight -font $bigLabFont +label .r.lab -text "Register File" -font $bigLabFont -height $sectionHeight +pack .r.lab -in .r -side top +# Set up top row control panel (disabled) +# frame .r.cntl +# pack .r.cntl -fill x -in .r +# label .r.labreg -text "Register" -width 10 +# entry .r.regid -width 3 -relief sunken -textvariable regId -font $dpyFont +# label .r.labval -text "Value" -width 10 +# entry .r.regval -width 8 -relief sunken -textvariable regVal -font $dpyFont +# button .r.doset -text "Set" -command {setReg $regId $regVal 1} -width 6 +# button .r.c -text "Clear" -command clearReg -width 6 +# pack .r.labreg .r.regid .r.labval .r.regval .r.doset .r.c -in .r.cntl -side left + +set regnames [list "%rax" "%rcx" "%rdx" "%rbx" "%rsp" "%rbp" "%rsi" "%rdi" "%r8 " "%r9 " "%r10" "%r11" "%r12" "%r13" "%r14" ""] + +# Create rows of register labels and displays +for {set j 0} {$j < 3} {incr j 1} { + frame .r.labels$j + pack .r.labels$j -side top -in .r + + for {set c 0} {$c < 5} {incr c 1} { + set i [expr $j * 5 + $c] + label .r.lab$i -width 16 -font $dpyFont -text [lindex $regnames $i] + pack .r.lab$i -in .r.labels$j -side left + } + + # Create Row of Register Entries + frame .r.row$j + pack .r.row$j -side top -in .r + + # Create 5 registers + for {set c 0} {$c < 5} {incr c 1} { + set i [expr $j * 5 + $c] + if {$i == 15} { + label .r.reg$i -width 16 -font $dpyFont -text "" + } else { + label .r.reg$i -width 16 -font $dpyFont -relief ridge \ + -bg $normalBg + } + pack .r.reg$i -in .r.row$j -side left + } + +} + +############################################################################## +# Main Control Panel # +############################################################################## +# +# Set the simulator name (defined in simname in ssim.c) +# as the title of the main window +# +wm title . $simname +#wm title . "Y86-64 Simulator" + +# Control Panel for simulator +set cntlBW 11 +frame .cntl +pack .cntl +button .cntl.quit -width $cntlBW -text Quit -command exit +button .cntl.run -width $cntlBW -text Go -command simGo +button .cntl.stop -width $cntlBW -text Stop -command simStop +button .cntl.step -width $cntlBW -text Step -command simStep +button .cntl.reset -width $cntlBW -text Reset -command simResetAll +pack .cntl.quit .cntl.run .cntl.stop .cntl.step .cntl.reset -in .cntl -side left +# Simulation speed control +scale .spd -label {Simulator Speed (10*log Hz)} -from -10 -to 30 -length 10c \ + -orient horizontal -command setSpeed +pack .spd + +# Simulation mode +set simMode forward + +# frame .md +# pack .md +# radiobutton .md.wedged -text Wedged -variable simMode \ +# -value wedged -width 10 -command {setSimMode wedged} +# radiobutton .md.stall -text Stall -variable simMode \ +# -value stall -width 10 -command {setSimMode stall} +# radiobutton .md.forward -text Forward -variable simMode \ +# -value forward -width 10 -command {setSimMode forward} +# pack .md.wedged .md.stall .md.forward -in .md -side left + +# simDelay defines #milliseconds for each cycle of simulator +# Initial value is 1000ms +set simDelay 1000 +# Set delay based on rate expressed in log(Hz) +proc setSpeed {rate} { + global simDelay + set simDelay [expr round(1000 / pow(10,$rate/10.0))] +} + +# Global variables controlling simulator execution +# Should simulator be running now? +set simGoOK 0 + +proc simStop {} { + global simGoOK + set simGoOK 0 +} + +proc simStep {} { + global simStat + set simStat [simRun 1] +} + +proc simGo {} { + global simGoOK simDelay simStat + set simGoOK 1 + # Disable the Go and Step buttons + # Enable the Stop button + while {$simGoOK} { + # run the simulator 1 cycle + after $simDelay + set simStat [simRun 1] + if {$simStat != "AOK" && $simStat != "BUB"} {set simGoOK 0} + update + } + # Disable the Stop button + # Enable the Go and Step buttons +} + +############################################################################## +# Processor State display # +############################################################################## + +# Overall width of pipe register display +set procWidth 60 +set procHeight 1 +set labWidth 8 + +# Add labeled display to window +proc addDisp {win width name} { + global dpyFont labFont + set lname [string tolower $name] + frame $win.$lname + pack $win.$lname -in $win -side left + label $win.$lname.t -text $name -font $labFont + label $win.$lname.c -width $width -font $dpyFont -bg white -relief ridge + pack $win.$lname.t $win.$lname.c -in $win.$lname -side top + return [list $win.$lname.c] +} + +# Set text in display row +proc setDisp {wins txts} { + for {set i 0} {$i < [llength $wins] && $i < [llength $txts]} {incr i} { + set win [lindex $wins $i] + set txt [lindex $txts $i] + $win config -text $txt + } +} + +frame .p -width $procWidth +pack .p -in . -side bottom +label .p.lab -text "Processor State" -height $sectionHeight -font $bigLabFont +pack .p.lab -in .p -side top +label .p.mem -text "Memory Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +label .p.ex -text "Execute Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +label .p.id -text "Decode Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +label .p.if -text "Fetch Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +label .p.pcc -text "PC Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +# Mem +frame .p.m +# Execute +frame .p.e +# Decode +frame .p.d +# Fetch +frame .p.f +# PC +frame .p.pc +# Prev +frame .p.prev +pack .p.m .p.mem .p.e .p.ex .p.d .p.id .p.f .p.if .p.pc .p.pcc .p.prev -in .p -side top -anchor w -expand 1 + +# Take list of lists, and transpose nesting +# Assumes all lists are of same length +proc ltranspose {inlist} { + set result {} + for {set i 0} {$i < [llength [lindex $inlist 0]]} {incr i} { + set nlist {} + for {set j 0} {$j < [llength $inlist]} {incr j} { + set ele [lindex [lindex $inlist $j] $i] + set nlist [concat $nlist [list $ele]] + } + set result [concat $result [list $nlist]] + } + return $result +} + +# Fields in PREV display +# Total size = 57 +set pwins(PREV) [ltranspose \ + [list [addDisp .p.prev 3 pCnd] \ + [addDisp .p.prev 6 pInstr] \ + [addDisp .p.prev 16 pValC] \ + [addDisp .p.prev 16 pValM] \ + [addDisp .p.prev 16 pValP]]] + + +# Fields in PC display +# Total size = 16 +set pwins(PC) [ltranspose [list [addDisp .p.pc 16 PC]]] + +# Fetch display +# Total size = 6+4+4+16+16 = 46 +set pwins(F) [ltranspose \ + [list [addDisp .p.f 6 Instr] \ + [addDisp .p.f 4 rA]\ + [addDisp .p.f 4 rB] \ + [addDisp .p.f 16 valC] \ + [addDisp .p.f 16 valP]]] + +# Decode Display +# Total size = 16+16+4+4+4+4 = 48 +set pwins(D) [ltranspose \ + [list [addDisp .p.d 16 valA] \ + [addDisp .p.d 16 valB] \ + [addDisp .p.d 4 dstE] \ + [addDisp .p.d 4 dstM] \ + [addDisp .p.d 4 srcA] \ + [addDisp .p.d 4 srcB]]] + +# Execute Display +# Total size = 3+16 = 19 +set pwins(E) [ltranspose \ + [list [addDisp .p.e 3 Cnd] \ + [addDisp .p.e 16 valE]]] + +# Memory Display +# Total size = 16 +set pwins(M) [ltranspose \ + [list [addDisp .p.m 16 valM]]] + +# update status line for specified proc register +proc updateStage {name txts} { + set Name [string toupper $name] + global pwins + set wins [lindex $pwins($Name) 0] + setDisp $wins $txts +} + +########################################################################## +# Instruction Display # +########################################################################## + +toplevel .c +wm title .c "Program Code" +frame .c.cntl +pack .c.cntl -in .c -side top -anchor w +label .c.filelab -width 10 -text "File" +entry .c.filename -width 20 -relief sunken -textvariable codeFile \ + -font $dpyFont -bg white +button .c.loadbutton -width $cntlBW -command {loadCode $codeFile} -text Load +pack .c.filelab .c.filename .c.loadbutton -in .c.cntl -side left + +proc clearCode {} { + simLabel {} {} + destroy .c.t + destroy .c.tr +} + +proc createCode {} { + # Create Code Structure + frame .c.t + pack .c.t -in .c -side top -anchor w + frame .c.tr + pack .c.tr -in .c.t -side top -anchor nw +} + +proc loadCode {file} { + # Kill old code window + clearCode + # Create new one + createCode + simCode $file + simResetAll +} + +# Start with initial code window, even though it will be destroyed. +createCode + +# Add a line of code to the display +proc addCodeLine {line addr op text} { + global codeRowCount + # Create new line in display + global codeFont + frame .c.tr.$addr + pack .c.tr.$addr -in .c.tr -side top -anchor w + label .c.tr.$addr.a -width 6 -text [format "0x%x" $addr] -font $codeFont + label .c.tr.$addr.i -width 20 -text $op -font $codeFont + label .c.tr.$addr.s -width 2 -text "" -font $codeFont -bg white + label .c.tr.$addr.t -text $text -font $codeFont + pack .c.tr.$addr.a .c.tr.$addr.i .c.tr.$addr.s \ + .c.tr.$addr.t -in .c.tr.$addr -side left +} + +# Keep track of which instructions have stage labels + +set oldAddr {} + +proc simLabel {addrs labs} { + global oldAddr + set newAddr {} + # Clear away any old labels + foreach a $oldAddr { + .c.tr.$a.s config -text "" + } + for {set i 0} {$i < [llength $addrs]} {incr i} { + set a [lindex $addrs $i] + set t [lindex $labs $i] + if {[winfo exists .c.tr.$a]} { + .c.tr.$a.s config -text $t + set newAddr [concat $newAddr $a] + } + } + set oldAddr $newAddr +} + +proc simResetAll {} { + global simStat + set simStat "AOK" + simReset + simLabel {} {} + clearMem +} + +############################################################################### +# Memory Display # +############################################################################### +toplevel .m +wm title .m "Memory Contents" +frame .m.t +pack .m.t -in .m -side top -anchor w + +label .m.t.lab -width 6 -font $dpyFont -text " " +pack .m.t.lab -in .m.t -side left +for {set i 0} {$i < 16} {incr i 8} { + label .m.t.a$i -width 16 -font $dpyFont -text [format " 0x---%x" [expr $i % 16]] + pack .m.t.a$i -in .m.t -side left +} + + +# Keep track of range of addresses currently displayed +set minAddr 0 +set memCnt 0 +set haveMem 0 + +proc createMem {nminAddr nmemCnt} { + global minAddr memCnt haveMem codeFont dpyFont normalBg + set minAddr $nminAddr + set memCnt $nmemCnt + + if { $haveMem } { destroy .m.e } + + # Create Memory Structure + frame .m.e + set haveMem 1 + pack .m.e -in .m -side top -anchor w + # Now fill it with values + for {set i 0} {$i < $memCnt} {incr i 16} { + set addr [expr $minAddr + $i] + + frame .m.e.r$i + pack .m.e.r$i -side bottom -in .m.e + label .m.e.r$i.lab -width 6 -font $dpyFont -text [format "0x%.3x-" [expr $addr / 16]] + pack .m.e.r$i.lab -in .m.e.r$i -side left + + for {set j 0} {$j < 16} {incr j 8} { + set a [expr $addr + $j] + label .m.e.v$a -width 16 -font $dpyFont -relief ridge \ + -bg $normalBg + pack .m.e.v$a -in .m.e.r$i -side left + } + } +} + +proc setMem {Addr Val} { + global minAddr memCnt + if {$Addr < $minAddr || $Addr > [expr $minAddr + $memCnt]} { + error "Memory address $Addr out of range" + } + .m.e.v$Addr config -text [format %16x $Val] +} + +proc clearMem {} { + destroy .m.e + createMem 0 0 +} + + + +############################################################################### +# Command Line Initialization # +############################################################################### + +# Get code file name from input + +# Find file with specified extension +proc findFile {tlist ext} { + foreach t $tlist { + if {[string match "*.$ext" $t]} {return $t} + } + return "" +} + + +set codeFile [findFile $argv yo] +if {$codeFile != ""} { loadCode $codeFile} diff --git a/arch/sim/seq/seq-full.hcl b/arch/sim/seq/seq-full.hcl new file mode 100644 index 0000000..4f592b1 --- /dev/null +++ b/arch/sim/seq/seq-full.hcl @@ -0,0 +1,224 @@ +#/* $begin seq-all-hcl */ +#################################################################### +# HCL Description of Control for Single Cycle Y86-64 Processor SEQ # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2010 # +#################################################################### + +## Your task is to implement the iaddq instruction +## The file contains a declaration of the icodes +## for iaddq (IIADDQ) +## Your job is to add the rest of the logic to make it work + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'word_t gen_pc(){return 0;}' +quote 'int main(int argc, char *argv[])' +quote ' {plusmode=0;return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' +# Instruction code for iaddq instruction +wordsig IIADDQ 'I_IADDQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced explicitly ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ##### +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic #################### + +##### Fetch stage inputs ##### +wordsig pc 'pc' # Program counter +##### Fetch stage computations ##### +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig icode 'icode' # Instruction control code +wordsig ifun 'ifun' # Instruction function +wordsig rA 'ra' # rA field from instruction +wordsig rB 'rb' # rB field from instruction +wordsig valC 'valc' # Constant from instruction +wordsig valP 'valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Decode stage computations ##### +wordsig valA 'vala' # Value from register A port +wordsig valB 'valb' # Value from register B port + +##### Execute stage computations ##### +wordsig valE 'vale' # Value computed by ALU +boolsig Cnd 'cond' # Branch test + +##### Memory stage computations ##### +wordsig valM 'valm' # Value read from memory +boolsig dmem_error 'dmem_error' # Error signal from data memory + + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +# Determine instruction code +word icode = [ + imem_error: INOP; + 1: imem_icode; # Default: get from instruction memory +]; + +# Determine instruction function +word ifun = [ + imem_error: FNONE; + 1: imem_ifun; # Default: get from instruction memory +]; + +bool instr_valid = icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ, IIADDQ }; + +# Does fetched instruction require a regid byte? +bool need_regids = + icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL, IIADDQ }; + +################ Decode Stage ################################### + +## What register should be used as the A source? +word srcA = [ + icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : rA; + icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word srcB = [ + icode in { IOPQ, IRMMOVQ, IMRMOVQ, IIADDQ } : rB; + icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word dstE = [ + icode in { IRRMOVQ } && Cnd : rB; + icode in { IIRMOVQ, IOPQ, IIADDQ } : rB; + icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word dstM = [ + icode in { IMRMOVQ, IPOPQ } : rA; + 1 : RNONE; # Don't write any register +]; + +################ Execute Stage ################################### + +## Select input A to ALU +word aluA = [ + icode in { IRRMOVQ, IOPQ } : valA; + icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ } : valC; + icode in { ICALL, IPUSHQ } : -8; + icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ, IIADDQ } : valB; + icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + icode == IOPQ : ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = icode in { IOPQ, IIADDQ }; + +################ Memory Stage ################################### + +## Set read control signal +bool mem_read = icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = icode in { IRMMOVQ, IPUSHQ, ICALL }; + +## Select memory address +word mem_addr = [ + icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : valE; + icode in { IPOPQ, IRET } : valA; + # Other instructions don't need address +]; + +## Select memory input data +word mem_data = [ + # Value from register + icode in { IRMMOVQ, IPUSHQ } : valA; + # Return PC + icode == ICALL : valP; + # Default: Don't write anything +]; + +## Determine instruction status +word Stat = [ + imem_error || dmem_error : SADR; + !instr_valid: SINS; + icode == IHALT : SHLT; + 1 : SAOK; +]; + +################ Program Counter Update ############################ + +## What address should instruction be fetched at + +word new_pc = [ + # Call. Use instruction constant + icode == ICALL : valC; + # Taken branch. Use instruction constant + icode == IJXX && Cnd : valC; + # Completion of RET instruction. Use value from stack + icode == IRET : valM; + # Default: Use incremented PC + 1 : valP; +]; +#/* $end seq-all-hcl */ diff --git a/arch/sim/seq/seq-std.hcl b/arch/sim/seq/seq-std.hcl new file mode 100644 index 0000000..9ee68ad --- /dev/null +++ b/arch/sim/seq/seq-std.hcl @@ -0,0 +1,217 @@ +#/* $begin seq-all-hcl */ +#################################################################### +# HCL Description of Control for Single Cycle Y86-64 Processor SEQ # +# Copyright (C) Randal E. Bryant, David R. O'Hallaron, 2010 # +#################################################################### + +#################################################################### +# C Include's. Don't alter these # +#################################################################### + +quote '#include ' +quote '#include "isa.h"' +quote '#include "sim.h"' +quote 'int sim_main(int argc, char *argv[]);' +quote 'word_t gen_pc(){return 0;}' +quote 'int main(int argc, char *argv[])' +quote ' {plusmode=0;return sim_main(argc,argv);}' + +#################################################################### +# Declarations. Do not change/remove/delete any of these # +#################################################################### + +##### Symbolic representation of Y86-64 Instruction Codes ############# +wordsig INOP 'I_NOP' +wordsig IHALT 'I_HALT' +wordsig IRRMOVQ 'I_RRMOVQ' +wordsig IIRMOVQ 'I_IRMOVQ' +wordsig IRMMOVQ 'I_RMMOVQ' +wordsig IMRMOVQ 'I_MRMOVQ' +wordsig IOPQ 'I_ALU' +wordsig IJXX 'I_JMP' +wordsig ICALL 'I_CALL' +wordsig IRET 'I_RET' +wordsig IPUSHQ 'I_PUSHQ' +wordsig IPOPQ 'I_POPQ' + +##### Symbolic represenations of Y86-64 function codes ##### +wordsig FNONE 'F_NONE' # Default function code + +##### Symbolic representation of Y86-64 Registers referenced explicitly ##### +wordsig RRSP 'REG_RSP' # Stack Pointer +wordsig RNONE 'REG_NONE' # Special value indicating "no register" + +##### ALU Functions referenced explicitly ##### +wordsig ALUADD 'A_ADD' # ALU should add its arguments + +##### Possible instruction status values ##### +wordsig SAOK 'STAT_AOK' # Normal execution +wordsig SADR 'STAT_ADR' # Invalid memory address +wordsig SINS 'STAT_INS' # Invalid instruction +wordsig SHLT 'STAT_HLT' # Halt instruction encountered + +##### Signals that can be referenced by control logic #################### + +##### Fetch stage inputs ##### +wordsig pc 'pc' # Program counter +##### Fetch stage computations ##### +wordsig imem_icode 'imem_icode' # icode field from instruction memory +wordsig imem_ifun 'imem_ifun' # ifun field from instruction memory +wordsig icode 'icode' # Instruction control code +wordsig ifun 'ifun' # Instruction function +wordsig rA 'ra' # rA field from instruction +wordsig rB 'rb' # rB field from instruction +wordsig valC 'valc' # Constant from instruction +wordsig valP 'valp' # Address of following instruction +boolsig imem_error 'imem_error' # Error signal from instruction memory +boolsig instr_valid 'instr_valid' # Is fetched instruction valid? + +##### Decode stage computations ##### +wordsig valA 'vala' # Value from register A port +wordsig valB 'valb' # Value from register B port + +##### Execute stage computations ##### +wordsig valE 'vale' # Value computed by ALU +boolsig Cnd 'cond' # Branch test + +##### Memory stage computations ##### +wordsig valM 'valm' # Value read from memory +boolsig dmem_error 'dmem_error' # Error signal from data memory + + +#################################################################### +# Control Signal Definitions. # +#################################################################### + +################ Fetch Stage ################################### + +# Determine instruction code +word icode = [ + imem_error: INOP; + 1: imem_icode; # Default: get from instruction memory +]; + +# Determine instruction function +word ifun = [ + imem_error: FNONE; + 1: imem_ifun; # Default: get from instruction memory +]; + +bool instr_valid = icode in + { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ, + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ }; + +# Does fetched instruction require a regid byte? +bool need_regids = + icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, + IIRMOVQ, IRMMOVQ, IMRMOVQ }; + +# Does fetched instruction require a constant word? +bool need_valC = + icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL }; + +################ Decode Stage ################################### + +## What register should be used as the A source? +word srcA = [ + icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : rA; + icode in { IPOPQ, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the B source? +word srcB = [ + icode in { IOPQ, IRMMOVQ, IMRMOVQ } : rB; + icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't need register +]; + +## What register should be used as the E destination? +word dstE = [ + icode in { IRRMOVQ } && Cnd : rB; + icode in { IIRMOVQ, IOPQ} : rB; + icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP; + 1 : RNONE; # Don't write any register +]; + +## What register should be used as the M destination? +word dstM = [ + icode in { IMRMOVQ, IPOPQ } : rA; + 1 : RNONE; # Don't write any register +]; + +################ Execute Stage ################################### + +## Select input A to ALU +word aluA = [ + icode in { IRRMOVQ, IOPQ } : valA; + icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : valC; + icode in { ICALL, IPUSHQ } : -8; + icode in { IRET, IPOPQ } : 8; + # Other instructions don't need ALU +]; + +## Select input B to ALU +word aluB = [ + icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, + IPUSHQ, IRET, IPOPQ } : valB; + icode in { IRRMOVQ, IIRMOVQ } : 0; + # Other instructions don't need ALU +]; + +## Set the ALU function +word alufun = [ + icode == IOPQ : ifun; + 1 : ALUADD; +]; + +## Should the condition codes be updated? +bool set_cc = icode in { IOPQ }; + +################ Memory Stage ################################### + +## Set read control signal +bool mem_read = icode in { IMRMOVQ, IPOPQ, IRET }; + +## Set write control signal +bool mem_write = icode in { IRMMOVQ, IPUSHQ, ICALL }; + +## Select memory address +word mem_addr = [ + icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : valE; + icode in { IPOPQ, IRET } : valA; + # Other instructions don't need address +]; + +## Select memory input data +word mem_data = [ + # Value from register + icode in { IRMMOVQ, IPUSHQ } : valA; + # Return PC + icode == ICALL : valP; + # Default: Don't write anything +]; + +## Determine instruction status +word Stat = [ + imem_error || dmem_error : SADR; + !instr_valid: SINS; + icode == IHALT : SHLT; + 1 : SAOK; +]; + +################ Program Counter Update ############################ + +## What address should instruction be fetched at + +word new_pc = [ + # Call. Use instruction constant + icode == ICALL : valC; + # Taken branch. Use instruction constant + icode == IJXX && Cnd : valC; + # Completion of RET instruction. Use value from stack + icode == IRET : valM; + # Default: Use incremented PC + 1 : valP; +]; +#/* $end seq-all-hcl */ diff --git a/arch/sim/seq/seq.tcl b/arch/sim/seq/seq.tcl new file mode 100644 index 0000000..2917a36 --- /dev/null +++ b/arch/sim/seq/seq.tcl @@ -0,0 +1,540 @@ + +########################################################################## +# Parsing of command line flags # +########################################################################## + +proc flagVal {flag default} { + global argv + foreach t $argv { + if {[string match "-$flag*" $t]} {return [string range $t 2 end]} + } + return $default +} + +proc findFlag {flag} { + global argv + foreach t $argv { + if {[string match "-$flag" $t]} {return 1} + } + return 0 +} + +########################################################################## +# Register File Implementation. Shown as array of 8 columns # +########################################################################## + + +# Font used to display register contents +set fontSize [expr 10 * [flagVal "f" 12]] +set codeFontSize [expr 10 * [flagVal "c" 10]] +set labFontSize [expr 10 * [flagVal "l" 10]] +set bigFontSize [expr 10 * [flagVal "b" 16]] +set dpyFont "*-courier-medium-r-normal--*-$fontSize-*-*-*-*-*-*" +set labFont "*-helvetica-medium-r-normal--*-$labFontSize-*-*-*-*-*-*" +set bigLabFont "*-helvetica-bold-r-normal--*-$bigFontSize-*-*-*-*-*-*" +set codeFont "*-courier-medium-r-normal--*-$codeFontSize-*-*-*-*-*-*" +# Background Color of normal register +set normalBg white +# Background Color of highlighted register +set specialBg LightSkyBlue + +# Height of titles separating major sections of control panel +set sectionHeight 2 + +# How many rows of code do I display +set codeRowCount [flagVal "r" 50] + +# Keep track of previous highlighted register +set lastId -1 +proc setReg {id val highlight} { + global lastId normalBg specialBg + if {$lastId >= 0} { + .r.reg$lastId config -bg $normalBg + set lastId -1 + } + if {$id < 0 || $id >= 15} { + error "Invalid Register ($id)" + } + .r.reg$id config -text [format %16x $val] + if {$highlight} { + uplevel .r.reg$id config -bg $specialBg + set lastId $id + } +} + +# Clear all registers +proc clearReg {} { + global lastId normalBg + if {$lastId >= 0} { + .r.reg$lastId config -bg $normalBg + set lastId -1 + } + for {set i 0} {$i < 8} {incr i 1} { + .r.reg$i config -text "" + } +} + +# Set all 3 condition codes +proc setCC {zv cv ov} { + .cc.cc0 config -text [format %d $zv] + .cc.cc1 config -text [format %d $cv] + .cc.cc2 config -text [format %d $ov] +} + + +### Create display for misc. state +frame .flags +pack .flags -in . -side bottom + +############################################################################## +# Status Display # +############################################################################## + +set simStat "AOK" +# Line to display simulation status +frame .stat +pack .stat -in .flags -side left +label .stat.statlab -width 7 -text "Stat" -font $bigLabFont -height $sectionHeight +label .stat.statdpy -width 3 -font $dpyFont -relief ridge -bg white -textvariable simStat +label .stat.fill -width 6 -text "" +pack .stat.statlab .stat.statdpy .stat.fill -in .stat -side left +############################################################################## +# Condition Code Display # +############################################################################## +# Create Window for condition codes +frame .cc +pack .cc -in .flags -side right + +label .cc.lab -text "Condition Codes" -font $bigLabFont -height $sectionHeight +pack .cc.lab -in .cc -side left + + +set ccnames [list "Z" "S" "O"] + +# Create Row of CC Labels +for {set i 0} {$i < 3} {incr i 1} { + label .cc.lab$i -width 1 -font $dpyFont -text [lindex $ccnames $i] + pack .cc.lab$i -in .cc -side left + label .cc.cc$i -width 1 -font $dpyFont -relief ridge -bg $normalBg + pack .cc.cc$i -in .cc -side left +} + +############################################################################## +# Register Display # +############################################################################## + + +# Create Window for registers +frame .r +pack .r -in . -side bottom +# Following give separate window for register file +# toplevel .r +# wm title .r "Register File" +label .r.lab -text "Register File" -font $bigLabFont -height $sectionHeight +pack .r.lab -in .r -side top +# Set up top row control panel (disabled) +# frame .r.cntl +# pack .r.cntl -fill x -in .r +# label .r.labreg -text "Register" -width 10 +# entry .r.regid -width 3 -relief sunken -textvariable regId -font $dpyFont +# label .r.labval -text "Value" -width 10 +# entry .r.regval -width 8 -relief sunken -textvariable regVal -font $dpyFont +# button .r.doset -text "Set" -command {setReg $regId $regVal 1} -width 6 +# button .r.c -text "Clear" -command clearReg -width 6 +# pack .r.labreg .r.regid .r.labval .r.regval .r.doset .r.c -in .r.cntl -side left + +set regnames [list "%rax" "%rcx" "%rdx" "%rbx" "%rsp" "%rbp" "%rsi" "%rdi" "%r8 " "%r9 " "%r10" "%r11" "%r12" "%r13" "%r14" ""] + +# Create rows of register labels and displays +for {set j 0} {$j < 3} {incr j 1} { + frame .r.labels$j + pack .r.labels$j -side top -in .r + + for {set c 0} {$c < 5} {incr c 1} { + set i [expr $j * 5 + $c] + label .r.lab$i -width 16 -font $dpyFont -text [lindex $regnames $i] + pack .r.lab$i -in .r.labels$j -side left + } + + # Create Row of Register Entries + frame .r.row$j + pack .r.row$j -side top -in .r + + # Create 5 registers + for {set c 0} {$c < 5} {incr c 1} { + set i [expr $j * 5 + $c] + if {$i == 15} { + label .r.reg$i -width 16 -font $dpyFont -text "" + } else { + label .r.reg$i -width 16 -font $dpyFont -relief ridge \ + -bg $normalBg + } + pack .r.reg$i -in .r.row$j -side left + } + +} + +############################################################################## +# Main Control Panel # +############################################################################## + +# +# Set the simulator name (defined in simname in ssim.c) +# as the title of the main window +# +wm title . $simname + +# Control Panel for simulator +set cntlBW 11 +frame .cntl +pack .cntl +button .cntl.quit -width $cntlBW -text Quit -command exit +button .cntl.run -width $cntlBW -text Go -command simGo +button .cntl.stop -width $cntlBW -text Stop -command simStop +button .cntl.step -width $cntlBW -text Step -command simStep +button .cntl.reset -width $cntlBW -text Reset -command simResetAll +pack .cntl.quit .cntl.run .cntl.stop .cntl.step .cntl.reset -in .cntl -side left +# Simulation speed control +scale .spd -label {Simulator Speed (10*log Hz)} -from -10 -to 30 -length 10c \ + -orient horizontal -command setSpeed +pack .spd + +# Simulation mode +set simMode forward + +# frame .md +# pack .md +# radiobutton .md.wedged -text Wedged -variable simMode \ +# -value wedged -width 10 -command {setSimMode wedged} +# radiobutton .md.stall -text Stall -variable simMode \ +# -value stall -width 10 -command {setSimMode stall} +# radiobutton .md.forward -text Forward -variable simMode \ +# -value forward -width 10 -command {setSimMode forward} +# pack .md.wedged .md.stall .md.forward -in .md -side left + +# simDelay defines #milliseconds for each cycle of simulator +# Initial value is 1000ms +set simDelay 1000 +# Set delay based on rate expressed in log(Hz) +proc setSpeed {rate} { + global simDelay + set simDelay [expr round(1000 / pow(10,$rate/10.0))] +} + +# Global variables controlling simulator execution +# Should simulator be running now? +set simGoOK 0 + +proc simStop {} { + global simGoOK + set simGoOK 0 +} + +proc simStep {} { + global simStat + set simStat [simRun 1] +} + +proc simGo {} { + global simGoOK simDelay simStat + set simGoOK 1 + # Disable the Go and Step buttons + # Enable the Stop button + while {$simGoOK} { + # run the simulator 1 cycle + after $simDelay + set simStat [simRun 1] + if {$simStat != "AOK" && $simStat != "BUB"} {set simGoOK 0} + update + } + # Disable the Stop button + # Enable the Go and Step buttons +} + +############################################################################## +# Processor State display # +############################################################################## + +# Overall width of pipe register display +set procWidth 60 +set procHeight 1 +set labWidth 8 + +# Add labeled display to window +proc addDisp {win width name} { + global dpyFont labFont + set lname [string tolower $name] + frame $win.$lname + pack $win.$lname -in $win -side left + label $win.$lname.t -text $name -font $labFont + label $win.$lname.c -width $width -font $dpyFont -bg white -relief ridge + pack $win.$lname.t $win.$lname.c -in $win.$lname -side top + return [list $win.$lname.c] +} + +# Set text in display row +proc setDisp {wins txts} { + for {set i 0} {$i < [llength $wins] && $i < [llength $txts]} {incr i} { + set win [lindex $wins $i] + set txt [lindex $txts $i] + $win config -text $txt + } +} + +frame .p -width $procWidth +pack .p -in . -side bottom +label .p.lab -text "Processor State" -font $bigLabFont -height $sectionHeight +pack .p.lab -in .p -side top +label .p.pc -text "PC Update Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +#label .p.wb -text "Writeback Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +label .p.mem -text "Memory Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +label .p.ex -text "Execute Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +label .p.id -text "Decode Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +label .p.if -text "Fetch Stage" -height $procHeight -font $bigLabFont -width $procWidth -bg NavyBlue -fg White +# New PC +frame .p.npc +# Mem +frame .p.m +# Execute +frame .p.e +# Decode +frame .p.d +# Fetch +frame .p.f +# Old PC +frame .p.opc +pack .p.npc .p.pc .p.m .p.mem .p.e .p.ex .p.d .p.id .p.f .p.if .p.opc -in .p -side top -anchor w -expand 1 + +# Take list of lists, and transpose nesting +# Assumes all lists are of same length +proc ltranspose {inlist} { + set result {} + for {set i 0} {$i < [llength [lindex $inlist 0]]} {incr i} { + set nlist {} + for {set j 0} {$j < [llength $inlist]} {incr j} { + set ele [lindex [lindex $inlist $j] $i] + set nlist [concat $nlist [list $ele]] + } + set result [concat $result [list $nlist]] + } + return $result +} + +# Fields in PC displayed +# Total size = 16 +set pwins(OPC) [ltranspose [list [addDisp .p.opc 16 PC]]] + +# Fetch display +# Total size = 6+4+4+16+16 = 46 +set pwins(F) [ltranspose \ + [list [addDisp .p.f 6 Instr] \ + [addDisp .p.f 4 rA]\ + [addDisp .p.f 4 rB] \ + [addDisp .p.f 16 valC] \ + [addDisp .p.f 16 valP]]] + +# Decode Display +# Total size = 16+16+4+4+4+4 = 48 +set pwins(D) [ltranspose \ + [list \ + [addDisp .p.d 16 valA] \ + [addDisp .p.d 16 valB] \ + [addDisp .p.d 4 dstE] \ + [addDisp .p.d 4 dstM] \ + [addDisp .p.d 4 srcA] \ + [addDisp .p.d 4 srcB]]] + + + + +# Execute Display +# Total size = 3+16 = 19 +set pwins(E) [ltranspose \ + [list [addDisp .p.e 3 Cnd] \ + [addDisp .p.e 16 valE]]] + +# Memory Display +# Total size = 16 +set pwins(M) [ltranspose \ + [list [addDisp .p.m 16 valM]]] + +# New PC Display +# Total Size = 16 +set pwins(NPC) [ltranspose \ + [list [addDisp .p.npc 16 newPC]]] + +# update status line for specified proc register +proc updateStage {name txts} { + set Name [string toupper $name] + global pwins + set wins [lindex $pwins($Name) 0] + setDisp $wins $txts +} + +########################################################################## +# Instruction Display # +########################################################################## + +toplevel .c +wm title .c "Program Code" +frame .c.cntl +pack .c.cntl -in .c -side top -anchor w +label .c.filelab -width 10 -text "File" +entry .c.filename -width 20 -relief sunken -textvariable codeFile \ + -font $dpyFont -bg white +button .c.loadbutton -width $cntlBW -command {loadCode $codeFile} -text Load +pack .c.filelab .c.filename .c.loadbutton -in .c.cntl -side left + +proc clearCode {} { + simLabel {} {} + destroy .c.t + destroy .c.tr +} + +proc createCode {} { + # Create Code Structure + frame .c.t + pack .c.t -in .c -side top -anchor w + frame .c.tr + pack .c.tr -in .c.t -side top -anchor nw +} + +proc loadCode {file} { + # Kill old code window + clearCode + # Create new one + createCode + simCode $file + simResetAll +} + +# Start with initial code window, even though it will be destroyed. +createCode + +# Add a line of code to the display +proc addCodeLine {line addr op text} { + global codeRowCount + # Create new line in display + global codeFont + frame .c.tr.$addr + pack .c.tr.$addr -in .c.tr -side top -anchor w + label .c.tr.$addr.a -width 6 -text [format "0x%x" $addr] -font $codeFont + label .c.tr.$addr.i -width 20 -text $op -font $codeFont + label .c.tr.$addr.s -width 2 -text "" -font $codeFont -bg white + label .c.tr.$addr.t -text $text -font $codeFont + pack .c.tr.$addr.a .c.tr.$addr.i .c.tr.$addr.s \ + .c.tr.$addr.t -in .c.tr.$addr -side left +} + +# Keep track of which instructions have stage labels + +set oldAddr {} + +proc simLabel {addrs labs} { + global oldAddr + set newAddr {} + # Clear away any old labels + foreach a $oldAddr { + .c.tr.$a.s config -text "" + } + for {set i 0} {$i < [llength $addrs]} {incr i} { + set a [lindex $addrs $i] + set t [lindex $labs $i] + if {[winfo exists .c.tr.$a]} { + .c.tr.$a.s config -text $t + set newAddr [concat $newAddr $a] + } + } + set oldAddr $newAddr +} + +proc simResetAll {} { + global simStat + set simStat "AOK" + simReset + simLabel {} {} + clearMem +} + +############################################################################### +# Memory Display # +############################################################################### +toplevel .m +wm title .m "Memory Contents" +frame .m.t +pack .m.t -in .m -side top -anchor w + +label .m.t.lab -width 6 -font $dpyFont -text " " +pack .m.t.lab -in .m.t -side left +for {set i 0} {$i < 16} {incr i 8} { + label .m.t.a$i -width 16 -font $dpyFont -text [format " 0x---%x" [expr $i % 16]] + pack .m.t.a$i -in .m.t -side left +} + + +# Keep track of range of addresses currently displayed +set minAddr 0 +set memCnt 0 +set haveMem 0 + +proc createMem {nminAddr nmemCnt} { + global minAddr memCnt haveMem codeFont dpyFont normalBg + set minAddr $nminAddr + set memCnt $nmemCnt + + if { $haveMem } { destroy .m.e } + + # Create Memory Structure + frame .m.e + set haveMem 1 + pack .m.e -in .m -side top -anchor w + # Now fill it with values + for {set i 0} {$i < $memCnt} {incr i 16} { + set addr [expr $minAddr + $i] + + frame .m.e.r$i + pack .m.e.r$i -side bottom -in .m.e + label .m.e.r$i.lab -width 6 -font $dpyFont -text [format "0x%.3x-" [expr $addr / 16]] + pack .m.e.r$i.lab -in .m.e.r$i -side left + + for {set j 0} {$j < 16} {incr j 8} { + set a [expr $addr + $j] + label .m.e.v$a -width 16 -font $dpyFont -relief ridge \ + -bg $normalBg + pack .m.e.v$a -in .m.e.r$i -side left + } + } +} + +proc setMem {Addr Val} { + global minAddr memCnt + if {$Addr < $minAddr || $Addr > [expr $minAddr + $memCnt]} { + error "Memory address $Addr out of range" + } + .m.e.v$Addr config -text [format %16x $Val] +} + +proc clearMem {} { + destroy .m.e + createMem 0 0 +} + + + +############################################################################### +# Command Line Initialization # +############################################################################### + +# Get code file name from input + +# Find file with specified extension +proc findFile {tlist ext} { + foreach t $tlist { + if {[string match "*.$ext" $t]} {return $t} + } + return "" +} + + +set codeFile [findFile $argv yo] +if {$codeFile != ""} { loadCode $codeFile} diff --git a/arch/sim/seq/sim.h b/arch/sim/seq/sim.h new file mode 100644 index 0000000..e5bf485 --- /dev/null +++ b/arch/sim/seq/sim.h @@ -0,0 +1,108 @@ + +/********** Defines **************/ + +/* Get ra out of one byte regid field */ +#define GET_RA(r) HI4(r) + +/* Get rb out of one byte regid field */ +#define GET_RB(r) LO4(r) + + +/************ Global state declaration ****************/ + +/* Determines whether running SEQ or SEQ+ */ +extern int plusmode; + +/* Both instruction and data memory */ +extern mem_t mem; + +/* Keep track of range of addresses that have been written */ +extern word_t minAddr; +extern word_t memCnt; + +/* Register file */ +extern mem_t reg; +/* Condition code register */ +extern cc_t cc; +/* Program counter */ +extern word_t pc; + +/* For seq+ */ +/* Results computed by previous instruction. + Used to compute PC in current instruction */ +extern byte_t prev_icode; +extern byte_t prev_ifun; +extern word_t prev_valc; +extern word_t prev_valm; +extern word_t prev_valp; +extern bool_t prev_bcond; + +/* Intermdiate stage values that must be used by control functions */ +extern byte_t imem_icode; +extern byte_t imem_ifun; +extern byte_t icode; +extern word_t ifun; +extern word_t ra; +extern word_t rb; +extern word_t valc; +extern word_t valp; +extern bool_t imem_error; +extern bool_t instr_valid; +extern word_t vala; +extern word_t valb; +extern word_t vale; +extern bool_t bcond; +extern bool_t cond; +extern word_t valm; +extern bool_t dmem_error; +extern byte_t status; + +/* Log file */ +extern FILE *dumpfile; + + +/* Sets the simulator name (called from main routine in HCL file) */ +void set_simname(char *name); + +/* Initialize simulator */ +void sim_init(); + +/* Reset simulator state, including register, instruction, and data memories */ +void sim_reset(); + +/* + Run processor until one of following occurs: + - An status error is encountered + - max_instr instructions have completed + + Return number of instructions executed. + if statusp nonnull, then will be set to status of final instruction + if ccp nonnull, then will be set to condition codes of final instruction +*/ +word_t sim_run(word_t max_instr, byte_t *statusp, cc_t *ccp); + +/* If dumpfile set nonNULL, lots of status info printed out */ +void sim_set_dumpfile(FILE *file); + +/* + * sim_log dumps a formatted string to the dumpfile, if it exists + * accepts variable argument list + */ +void sim_log( const char *format, ... ); + + +/******************* GUI Interface Functions **********************/ +#ifdef HAS_GUI + +void signal_register_clear(); + +void report_pc(word_t pc); + +void report_state(char *id, char *txt); + +void show_cc(cc_t cc); + +void create_memory_display(); +void set_memory(word_t addr, word_t val); +#endif + diff --git a/arch/sim/seq/ssim.c b/arch/sim/seq/ssim.c new file mode 100644 index 0000000..4cae5a9 --- /dev/null +++ b/arch/sim/seq/ssim.c @@ -0,0 +1,1169 @@ +/*********************************************************************** + * + * ssim.c - Sequential Y86-64 simulator + * + * Copyright (c) 2002, 2015. Bryant and D. O'Hallaron, All rights reserved. + * May not be used, modified, or copied without permission. + ***********************************************************************/ + +#include +#include +#include +#include +#include +#include "isa.h" +#include "sim.h" + +#define MAXBUF 1024 + +#ifdef HAS_GUI +#include +#endif /* HAS_GUI */ + +#define MAXARGS 128 +#define MAXBUF 1024 +#define TKARGS 3 + +/*************** + * Begin Globals + ***************/ + +/* Simulator name defined and initialized by the compiled HCL file */ +/* according to the -n argument supplied to hcl2c */ +extern char simname[]; + +/* SEQ=0, SEQ+=1. Modified by HCL main() */ +int plusmode = 0; + +/* Parameters modifed by the command line */ +int gui_mode = FALSE; /* Run in GUI mode instead of TTY mode? (-g) */ +char *object_filename; /* The input object file name. */ +FILE *object_file; /* Input file handle */ +bool_t verbosity = 2; /* Verbosity level [TTY only] (-v) */ +word_t instr_limit = 10000; /* Instruction limit [TTY only] (-l) */ +bool_t do_check = FALSE; /* Test with YIS? [TTY only] (-t) */ + +/************* + * End Globals + *************/ + + +/*************************** + * Begin function prototypes + ***************************/ + +static void usage(char *name); /* Print helpful usage message */ +static void run_tty_sim(); /* Run simulator in TTY mode */ + +#ifdef HAS_GUI +void addAppCommands(Tcl_Interp *interp); /* Add application-dependent commands */ +#endif /* HAS_GUI */ + +/************************* + * End function prototypes + *************************/ + + +/******************************************************************* + * Part 1: This part is the initial entry point that handles general + * initialization. It parses the command line and does any necessary + * setup to run in either TTY or GUI mode, and then starts the + * simulation. + *******************************************************************/ + +/* + * sim_main - main simulator routine. This function is called from the + * main() routine in the HCL file. + */ +int sim_main(int argc, char **argv) +{ + int i; + int c; + char *myargv[MAXARGS]; + + + /* Parse the command line arguments */ + while ((c = getopt(argc, argv, "htgl:v:")) != -1) { + switch(c) { + case 'h': + usage(argv[0]); + break; + case 'l': + instr_limit = atoll(optarg); + break; + case 'v': + verbosity = atoi(optarg); + if (verbosity < 0 || verbosity > 2) { + printf("Invalid verbosity %d\n", verbosity); + usage(argv[0]); + } + break; + case 't': + do_check = TRUE; + break; + case 'g': + gui_mode = TRUE; + break; + default: + printf("Invalid option '%c'\n", c); + usage(argv[0]); + break; + } + } + + + /* Do we have too many arguments? */ + if (optind < argc - 1) { + printf("Too many command line arguments:"); + for (i = optind; i < argc; i++) + printf(" %s", argv[i]); + printf("\n"); + usage(argv[0]); + } + + + /* The single unflagged argument should be the object file name */ + object_filename = NULL; + object_file = NULL; + if (optind < argc) { + object_filename = argv[optind]; + object_file = fopen(object_filename, "r"); + if (!object_file) { + fprintf(stderr, "Couldn't open object file %s\n", object_filename); + exit(1); + } + } + + + /* Run the simulator in GUI mode (-g flag) */ + if (gui_mode) { + +#ifndef HAS_GUI + printf("To run in GUI mode, you must recompile with the HAS_GUI constant defined.\n"); + exit(1); +#endif /* HAS_GUI */ + + /* In GUI mode, we must specify the object file on command line */ + if (!object_file) { + printf("Missing object file argument in GUI mode\n"); + usage(argv[0]); + } + + /* Build the command line for the GUI simulator */ + for (i = 0; i < TKARGS; i++) { + if ((myargv[i] = malloc(MAXBUF*sizeof(char))) == NULL) { + perror("malloc error"); + exit(1); + } + } + strcpy(myargv[0], argv[0]); + +#if 0 + printf("argv[0]=%s\n", argv[0]); + { + char buf[1000]; + getcwd(buf, 1000); + printf("cwd=%s\n", buf); + } +#endif + + if (plusmode == 0) /* SEQ */ + strcpy(myargv[1], "seq.tcl"); + else + strcpy(myargv[1], "seq+.tcl"); + strcpy(myargv[2], object_filename); + myargv[3] = NULL; + + /* Start the GUI simulator */ +#ifdef HAS_GUI + Tk_Main(TKARGS, myargv, Tcl_AppInit); +#endif /* HAS_GUI */ + exit(0); + } + + /* Otherwise, run the simulator in TTY mode (no -g flag) */ + run_tty_sim(); + + exit(0); +} + +/* + * run_tty_sim - Run the simulator in TTY mode + */ +static void run_tty_sim() +{ + word_t icount = 0; + status = STAT_AOK; + cc_t result_cc = 0; + word_t byte_cnt = 0; + mem_t mem0, reg0; + state_ptr isa_state = NULL; + + + /* In TTY mode, the default object file comes from stdin */ + if (!object_file) { + object_file = stdin; + } + + /* Initializations */ + if (verbosity >= 2) + sim_set_dumpfile(stdout); + sim_init(); + + /* Emit simulator name */ + printf("%s\n", simname); + + byte_cnt = load_mem(mem, object_file, 1); + if (byte_cnt == 0) { + fprintf(stderr, "No lines of code found\n"); + exit(1); + } else if (verbosity >= 2) { + printf("%lld bytes of code read\n", byte_cnt); + } + fclose(object_file); + if (do_check) { + isa_state = new_state(0); + free_mem(isa_state->r); + free_mem(isa_state->m); + isa_state->m = copy_mem(mem); + isa_state->r = copy_mem(reg); + isa_state->cc = cc; + } + + mem0 = copy_mem(mem); + reg0 = copy_mem(reg); + + + icount = sim_run(instr_limit, &status, &result_cc); + if (verbosity > 0) { + printf("%lld instructions executed\n", icount); + printf("Status = %s\n", stat_name(status)); + printf("Condition Codes: %s\n", cc_name(result_cc)); + printf("Changed Register State:\n"); + diff_reg(reg0, reg, stdout); + printf("Changed Memory State:\n"); + diff_mem(mem0, mem, stdout); + } + if (do_check) { + byte_t e = STAT_AOK; + int step; + bool_t match = TRUE; + + for (step = 0; step < instr_limit && e == STAT_AOK; step++) { + e = step_state(isa_state, stdout); + } + + if (diff_reg(isa_state->r, reg, NULL)) { + match = FALSE; + if (verbosity > 0) { + printf("ISA Register != Pipeline Register File\n"); + diff_reg(isa_state->r, reg, stdout); + } + } + if (diff_mem(isa_state->m, mem, NULL)) { + match = FALSE; + if (verbosity > 0) { + printf("ISA Memory != Pipeline Memory\n"); + diff_mem(isa_state->m, mem, stdout); + } + } + if (isa_state->cc != result_cc) { + match = FALSE; + if (verbosity > 0) { + printf("ISA Cond. Codes (%s) != Pipeline Cond. Codes (%s)\n", + cc_name(isa_state->cc), cc_name(result_cc)); + } + } + if (match) { + printf("ISA Check Succeeds\n"); + } else { + printf("ISA Check Fails\n"); + } + } +} + + + +/* + * usage - print helpful diagnostic information + */ +static void usage(char *name) +{ + printf("Usage: %s [-htg] [-l m] [-v n] file.yo\n", name); + printf("file.yo required in GUI mode, optional in TTY mode (default stdin)\n"); + printf(" -h Print this message\n"); + printf(" -g Run in GUI mode instead of TTY mode (default TTY)\n"); + printf(" -l m Set instruction limit to m [TTY mode only] (default %lld)\n", instr_limit); + printf(" -v n Set verbosity level to 0 <= n <= 2 [TTY mode only] (default %d)\n", verbosity); + printf(" -t Test result against ISA simulator (yis) [TTY mode only]\n"); + exit(0); +} + + + +/********************************************************* + * Part 2: This part contains the core simulator routines. + *********************************************************/ + +/********************** + * Begin Part 2 Globals + **********************/ + +/* + * Variables related to hardware units in the processor + */ +mem_t mem; /* Instruction and data memory */ +word_t minAddr = 0; +word_t memCnt = 0; + +/* Other processor state */ +mem_t reg; /* Register file */ +cc_t cc = DEFAULT_CC; /* Condition code register */ +cc_t cc_in = DEFAULT_CC; /* Input to condition code register */ + +/* + * SEQ+: Results computed by previous instruction. + * Used to compute PC in current instruction + */ +byte_t prev_icode = I_NOP; +byte_t prev_ifun = 0; +word_t prev_valc = 0; +word_t prev_valm = 0; +word_t prev_valp = 0; +bool_t prev_bcond = FALSE; + +byte_t prev_icode_in = I_NOP; +byte_t prev_ifun_in = 0; +word_t prev_valc_in = 0; +word_t prev_valm_in = 0; +word_t prev_valp_in = 0; +bool_t prev_bcond_in = FALSE; + + +/* Program Counter */ +word_t pc = 0; /* Program counter value */ +word_t pc_in = 0;/* Input to program counter */ + +/* Intermediate values */ +byte_t imem_icode = I_NOP; +byte_t imem_ifun = F_NONE; +byte_t icode = I_NOP; +word_t ifun = 0; +byte_t instr = HPACK(I_NOP, F_NONE); +word_t ra = REG_NONE; +word_t rb = REG_NONE; +word_t valc = 0; +word_t valp = 0; +bool_t imem_error; +bool_t instr_valid; + +word_t srcA = REG_NONE; +word_t srcB = REG_NONE; +word_t destE = REG_NONE; +word_t destM = REG_NONE; +word_t vala = 0; +word_t valb = 0; +word_t vale = 0; + +bool_t bcond = FALSE; +bool_t cond = FALSE; +word_t valm = 0; +bool_t dmem_error; + +bool_t mem_write = FALSE; +word_t mem_addr = 0; +word_t mem_data = 0; +byte_t status = STAT_AOK; + + +/* Values computed by control logic */ +word_t gen_pc(); /* SEQ+ */ +word_t gen_icode(); +word_t gen_ifun(); +word_t gen_need_regids(); +word_t gen_need_valC(); +word_t gen_instr_valid(); +word_t gen_srcA(); +word_t gen_srcB(); +word_t gen_dstE(); +word_t gen_dstM(); +word_t gen_aluA(); +word_t gen_aluB(); +word_t gen_alufun(); +word_t gen_set_cc(); +word_t gen_mem_addr(); +word_t gen_mem_data(); +word_t gen_mem_read(); +word_t gen_mem_write(); +word_t gen_Stat(); +word_t gen_new_pc(); + +/* Log file */ +FILE *dumpfile = NULL; + +#ifdef HAS_GUI +/* Representations of digits */ +static char digits[16] = + {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; +#endif /* HAS_GUI */ + +/******************** + * End Part 2 Globals + ********************/ + +#ifdef HAS_GUI + +/* Create string in hex/oct/binary format with leading zeros */ +/* bpd denotes bits per digit Should be in range 1-4, + bpw denotes bits per word.*/ +void wstring(uword_t x, int bpd, int bpw, char *str) +{ + int digit; + uword_t mask = ((uword_t) 1 << bpd) - 1; + for (digit = (bpw-1)/bpd; digit >= 0; digit--) { + uword_t val = (x >> (digit * bpd)) & mask; + *str++ = digits[val]; + } + *str = '\0'; +} + +/* used for formatting instructions */ +static char status_msg[128]; + +/* SEQ+ */ +static char *format_prev() +{ + char istring[17]; + char mstring[17]; + char pstring[17]; + wstring(prev_valc, 4, 64, istring); + wstring(prev_valm, 4, 64, mstring); + wstring(prev_valp, 4, 64, pstring); + sprintf(status_msg, "%c %s %s %s %s", + prev_bcond ? 'Y' : 'N', + iname(HPACK(prev_icode, prev_ifun)), + istring, mstring, pstring); + + return status_msg; +} + +static char *format_pc() +{ + char pstring[17]; + wstring(pc, 4, 64, pstring); + sprintf(status_msg, "%s", pstring); + return status_msg; +} + +static char *format_f() +{ + char valcstring[17]; + char valpstring[17]; + wstring(valc, 4, 64, valcstring); + wstring(valp, 4, 64, valpstring); + sprintf(status_msg, "%s %s %s %s %s", + iname(HPACK(icode, ifun)), + reg_name(ra), + reg_name(rb), + valcstring, + valpstring); + return status_msg; +} + +static char *format_d() +{ + char valastring[17]; + char valbstring[17]; + wstring(vala, 4, 64, valastring); + wstring(valb, 4, 64, valbstring); + sprintf(status_msg, "%s %s %s %s %s %s", + valastring, + valbstring, + reg_name(destE), + reg_name(destM), + reg_name(srcA), + reg_name(srcB)); + + return status_msg; +} + +static char *format_e() +{ + char valestring[17]; + wstring(vale, 4, 64, valestring); + sprintf(status_msg, "%c %s", + bcond ? 'Y' : 'N', + valestring); + return status_msg; +} + +static char *format_m() +{ + char valmstring[17]; + wstring(valm, 4, 64, valmstring); + sprintf(status_msg, "%s", valmstring); + return status_msg; +} + +static char *format_npc() +{ + char npcstring[17]; + wstring(pc_in, 4, 64, npcstring); + sprintf(status_msg, "%s", npcstring); + return status_msg; +} +#endif /* HAS_GUI */ + +/* Report system state */ +static void sim_report() { + +#ifdef HAS_GUI + if (gui_mode) { + report_pc(pc); + if (plusmode) { + report_state("PREV", format_prev()); + report_state("PC", format_pc()); + } else { + report_state("OPC", format_pc()); + } + report_state("F", format_f()); + report_state("D", format_d()); + report_state("E", format_e()); + report_state("M", format_m()); + if (!plusmode) { + report_state("NPC", format_npc()); + } + show_cc(cc); + } +#endif /* HAS_GUI */ + +} + +static int initialized = 0; +void sim_init() +{ + + /* Create memory and register files */ + initialized = 1; + mem = init_mem(MEM_SIZE); + reg = init_reg(); + sim_reset(); + clear_mem(mem); +} + +void sim_reset() +{ + if (!initialized) + sim_init(); + clear_mem(reg); + minAddr = 0; + memCnt = 0; + +#ifdef HAS_GUI + if (gui_mode) { + signal_register_clear(); + create_memory_display(); + sim_report(); + } +#endif + + if (plusmode) { + prev_icode = prev_icode_in = I_NOP; + prev_ifun = prev_ifun_in = 0; + prev_valc = prev_valc_in = 0; + prev_valm = prev_valm_in = 0; + prev_valp = prev_valp_in = 0; + prev_bcond = prev_bcond_in = FALSE; + pc = 0; + } else { + pc_in = 0; + } + cc = DEFAULT_CC; + cc_in = DEFAULT_CC; + destE = REG_NONE; + destM = REG_NONE; + mem_write = FALSE; + mem_addr = 0; + mem_data = 0; + + /* Reset intermediate values to clear display */ + icode = I_NOP; + ifun = 0; + instr = HPACK(I_NOP, F_NONE); + ra = REG_NONE; + rb = REG_NONE; + valc = 0; + valp = 0; + + srcA = REG_NONE; + srcB = REG_NONE; + destE = REG_NONE; + destM = REG_NONE; + vala = 0; + valb = 0; + vale = 0; + + cond = FALSE; + bcond = FALSE; + valm = 0; + + sim_report(); +} + +/* Update the processor state */ +static void update_state() +{ + if (plusmode) { + prev_icode = prev_icode_in; + prev_ifun = prev_ifun_in; + prev_valc = prev_valc_in; + prev_valm = prev_valm_in; + prev_valp = prev_valp_in; + prev_bcond = prev_bcond_in; + } else { + pc = pc_in; + } + cc = cc_in; + /* Writeback */ + if (destE != REG_NONE) + set_reg_val(reg, destE, vale); + if (destM != REG_NONE) + set_reg_val(reg, destM, valm); + + if (mem_write) { + /* Should have already tested this address */ + set_word_val(mem, mem_addr, mem_data); + sim_log("Wrote 0x%llx to address 0x%llx\n", mem_data, mem_addr); +#ifdef HAS_GUI + if (gui_mode) { + if (mem_addr % 8 != 0) { + /* Just did a misaligned write. + Need to display both words */ + word_t align_addr = mem_addr & ~0x3; + word_t val; + get_word_val(mem, align_addr, &val); + set_memory(align_addr, val); + align_addr+=8; + get_word_val(mem, align_addr, &val); + set_memory(align_addr, val); + } else { + set_memory(mem_addr, mem_data); + } + } +#endif /* HAS_GUI */ + } +} + +/* Execute one instruction */ +/* Return resulting status */ +static byte_t sim_step() +{ + word_t aluA; + word_t aluB; + word_t alufun; + + status = STAT_AOK; + imem_error = dmem_error = FALSE; + + update_state(); /* Update state from last cycle */ + + if (plusmode) { + pc = gen_pc(); + } + valp = pc; + instr = HPACK(I_NOP, F_NONE); + imem_error = !get_byte_val(mem, valp, &instr); + if (imem_error) { + sim_log("Couldn't fetch at address 0x%llx\n", valp); + } + imem_icode = HI4(instr); + imem_ifun = LO4(instr); + icode = gen_icode(); + ifun = gen_ifun(); + instr_valid = gen_instr_valid(); + valp++; + if (gen_need_regids()) { + byte_t regids; + if (get_byte_val(mem, valp, ®ids)) { + ra = GET_RA(regids); + rb = GET_RB(regids); + } else { + ra = REG_NONE; + rb = REG_NONE; + status = STAT_ADR; + sim_log("Couldn't fetch at address 0x%llx\n", valp); + } + valp++; + } else { + ra = REG_NONE; + rb = REG_NONE; + } + + if (gen_need_valC()) { + if (get_word_val(mem, valp, &valc)) { + } else { + valc = 0; + status = STAT_ADR; + sim_log("Couldn't fetch at address 0x%llx\n", valp); + } + valp+=8; + } else { + valc = 0; + } + sim_log("IF: Fetched %s at 0x%llx. ra=%s, rb=%s, valC = 0x%llx\n", + iname(HPACK(icode,ifun)), pc, reg_name(ra), reg_name(rb), valc); + + if (status == STAT_AOK && icode == I_HALT) { + status = STAT_HLT; + } + + srcA = gen_srcA(); + if (srcA != REG_NONE) { + vala = get_reg_val(reg, srcA); + } else { + vala = 0; + } + + srcB = gen_srcB(); + if (srcB != REG_NONE) { + valb = get_reg_val(reg, srcB); + } else { + valb = 0; + } + + cond = cond_holds(cc, ifun); + + destE = gen_dstE(); + destM = gen_dstM(); + + aluA = gen_aluA(); + aluB = gen_aluB(); + alufun = gen_alufun(); + vale = compute_alu(alufun, aluA, aluB); + cc_in = cc; + if (gen_set_cc()) + cc_in = compute_cc(alufun, aluA, aluB); + + bcond = cond && (icode == I_JMP); + + mem_addr = gen_mem_addr(); + mem_data = gen_mem_data(); + + + if (gen_mem_read()) { + dmem_error = dmem_error || !get_word_val(mem, mem_addr, &valm); + if (dmem_error) { + sim_log("Couldn't read at address 0x%llx\n", mem_addr); + } + } else + valm = 0; + + mem_write = gen_mem_write(); + if (mem_write) { + /* Do a test read of the data memory to make sure address is OK */ + word_t junk; + dmem_error = dmem_error || !get_word_val(mem, mem_addr, &junk); + } + + status = gen_Stat(); + + if (plusmode) { + prev_icode_in = icode; + prev_ifun_in = ifun; + prev_valc_in = valc; + prev_valm_in = valm; + prev_valp_in = valp; + prev_bcond_in = bcond; + } else { + /* Update PC */ + pc_in = gen_new_pc(); + } + sim_report(); + return status; +} + +/* + Run processor until one of following occurs: + - An error status is encountered in WB. + - max_instr instructions have completed through WB + + Return number of instructions executed. + if statusp nonnull, then will be set to status of final instruction + if ccp nonnull, then will be set to condition codes of final instruction +*/ +word_t sim_run(word_t max_instr, byte_t *statusp, cc_t *ccp) +{ + word_t icount = 0; + byte_t run_status = STAT_AOK; + while (icount < max_instr) { + run_status = sim_step(); + icount++; + if (run_status != STAT_AOK) + break; + } + if (statusp) + *statusp = run_status; + if (ccp) + *ccp = cc; + return icount; +} + +/* If dumpfile set nonNULL, lots of status info printed out */ +void sim_set_dumpfile(FILE *df) +{ + dumpfile = df; +} + +/* + * sim_log dumps a formatted string to the dumpfile, if it exists + * accepts variable argument list + */ +void sim_log( const char *format, ... ) { + if (dumpfile) { + va_list arg; + va_start( arg, format ); + vfprintf( dumpfile, format, arg ); + va_end( arg ); + } +} + + +/************************************************************* + * Part 3: This part contains simulation control for the TK + * simulator. + *************************************************************/ + +#ifdef HAS_GUI + +/********************** + * Begin Part 3 globals + **********************/ + +/* Hack for SunOS */ +extern int matherr(); +int *tclDummyMathPtr = (int *) matherr; + +static char tcl_msg[256]; + +/* Keep track of the TCL Interpreter */ +static Tcl_Interp *sim_interp = NULL; + +static mem_t post_load_mem; + +/********************** + * End Part 3 globals + **********************/ + + +/* function prototypes */ +int simResetCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); +int simLoadCodeCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); +int simLoadDataCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); +int simRunCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]); +void addAppCommands(Tcl_Interp *interp); + +/****************************************************************************** + * tcl command definitions + ******************************************************************************/ + +/* Implement command versions of the simulation functions */ +int simResetCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + sim_interp = interp; + if (argc != 1) { + interp->result = "No arguments allowed"; + return TCL_ERROR; + } + sim_reset(); + if (post_load_mem) { + free_mem(mem); + mem = copy_mem(post_load_mem); + } + interp->result = stat_name(STAT_AOK); + return TCL_OK; +} + +int simLoadCodeCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + FILE *object_file; + word_t code_count; + sim_interp = interp; + if (argc != 2) { + interp->result = "One argument required"; + return TCL_ERROR; + } + object_file = fopen(argv[1], "r"); + if (!object_file) { + sprintf(tcl_msg, "Couldn't open code file '%s'", argv[1]); + interp->result = tcl_msg; + return TCL_ERROR; + } + sim_reset(); + code_count = load_mem(mem, object_file, 0); + post_load_mem = copy_mem(mem); + sprintf(tcl_msg, "%lld", code_count); + interp->result = tcl_msg; + fclose(object_file); + return TCL_OK; +} + +int simLoadDataCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + FILE *data_file; + word_t word_count = 0; + interp->result = "Not implemented"; + return TCL_ERROR; + + + sim_interp = interp; + if (argc != 2) { + interp->result = "One argument required"; + return TCL_ERROR; + } + data_file = fopen(argv[1], "r"); + if (!data_file) { + sprintf(tcl_msg, "Couldn't open data file '%s'", argv[1]); + interp->result = tcl_msg; + return TCL_ERROR; + } + sprintf(tcl_msg, "%lld", word_count); + interp->result = tcl_msg; + fclose(data_file); + return TCL_OK; +} + + +int simRunCmd(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + word_t step_limit = 1; + byte_t run_status; + cc_t cc; + sim_interp = interp; + if (argc > 2) { + interp->result = "At most one argument allowed"; + return TCL_ERROR; + } + if (argc >= 2 && + (sscanf(argv[1], "%lld", &step_limit) != 1 || + step_limit < 0)) { + sprintf(tcl_msg, "Cannot run for '%s' cycles!", argv[1]); + interp->result = tcl_msg; + return TCL_ERROR; + } + sim_run(step_limit, &run_status, &cc); + interp->result = stat_name(run_status); + return TCL_OK; +} + +/****************************************************************************** + * registering the commands with tcl + ******************************************************************************/ + +void addAppCommands(Tcl_Interp *interp) +{ + sim_interp = interp; + Tcl_CreateCommand(interp, "simReset", (Tcl_CmdProc *) simResetCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "simCode", (Tcl_CmdProc *) simLoadCodeCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "simData", (Tcl_CmdProc *) simLoadDataCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "simRun", (Tcl_CmdProc *) simRunCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); +} + +/****************************************************************************** + * tcl functionality called from within C + ******************************************************************************/ + +/* Provide mechanism for simulator to update register display */ +void signal_register_update(reg_id_t r, word_t val) { + int code; + sprintf(tcl_msg, "setReg %d %lld 1", (int) r, (word_t) val); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to signal register set\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Provide mechanism for simulator to generate memory display */ +void create_memory_display() { + int code; + sprintf(tcl_msg, "createMem %lld %lld", minAddr, memCnt); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Command '%s' failed\n", tcl_msg); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } else { + word_t i; + for (i = 0; i < memCnt && code == TCL_OK; i+=8) { + word_t addr = minAddr+i; + word_t val; + if (!get_word_val(mem, addr, &val)) { + fprintf(stderr, "Out of bounds memory display\n"); + return; + } + sprintf(tcl_msg, "setMem %lld %lld", addr, val); + code = Tcl_Eval(sim_interp, tcl_msg); + } + if (code != TCL_OK) { + fprintf(stderr, "Couldn't set memory value\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } + } +} + +/* Provide mechanism for simulator to update memory value */ +void set_memory(word_t addr, word_t val) { + int code; + word_t nminAddr = minAddr; + word_t nmemCnt = memCnt; + + /* First see if we need to expand memory range */ + if (memCnt == 0) { + nminAddr = addr; + nmemCnt = 8; + } else if (addr < minAddr) { + nminAddr = addr; + nmemCnt = minAddr + memCnt - addr; + } else if (addr >= minAddr+memCnt) { + nmemCnt = addr-minAddr+8; + } + /* Now make sure nminAddr & nmemCnt are multiples of 16 */ + nmemCnt = ((nminAddr & 0xF) + nmemCnt + 0xF) & ~0xF; + nminAddr = nminAddr & ~0xF; + + if (nminAddr != minAddr || nmemCnt != memCnt) { + minAddr = nminAddr; + memCnt = nmemCnt; + create_memory_display(); + } else { + sprintf(tcl_msg, "setMem %lld %lld", addr, val); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Couldn't set memory value 0x%llx to 0x%llx\n", + addr, val); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } + } +} + +/* Provide mechanism for simulator to update condition code display */ +void show_cc(cc_t cc) +{ + int code; + sprintf(tcl_msg, "setCC %d %d %d", + GET_ZF(cc), GET_SF(cc), GET_OF(cc)); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to display condition codes\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Provide mechanism for simulator to clear register display */ +void signal_register_clear() { + int code; + code = Tcl_Eval(sim_interp, "clearReg"); + if (code != TCL_OK) { + fprintf(stderr, "Failed to signal register clear\n"); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Provide mechanism for simulator to report instructions as they are + read in +*/ + +void report_line(word_t line_no, word_t addr, char *hex, char *text) { + int code; + sprintf(tcl_msg, "addCodeLine %lld %lld {%s} {%s}", line_no, addr, hex, text); + code = Tcl_Eval(sim_interp, tcl_msg); + if (code != TCL_OK) { + fprintf(stderr, "Failed to report code line 0x%llx\n", addr); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + + +/* Provide mechanism for simulator to report which instruction + is being executed */ +void report_pc(word_t pc) +{ + int t_status; + char addr[18]; + char code[20]; + Tcl_DString cmd; + Tcl_DStringInit(&cmd); + Tcl_DStringAppend(&cmd, "simLabel ", -1); + Tcl_DStringStartSublist(&cmd); + sprintf(addr, "%llu", pc); + Tcl_DStringAppendElement(&cmd, addr); + + Tcl_DStringEndSublist(&cmd); + Tcl_DStringStartSublist(&cmd); + sprintf(code, "%s","*"); + Tcl_DStringAppend(&cmd, code, -1); + Tcl_DStringEndSublist(&cmd); + t_status = Tcl_Eval(sim_interp, Tcl_DStringValue(&cmd)); + if (t_status != TCL_OK) { + fprintf(stderr, "Failed to report code '%s'\n", code); + fprintf(stderr, "Error Message was '%s'\n", sim_interp->result); + } +} + +/* Report single line of stage state */ +void report_state(char *id, char *txt) +{ + int t_status; + sprintf(tcl_msg, "updateStage %s {%s}", id, txt); + t_status = Tcl_Eval(sim_interp, tcl_msg); + if (t_status != TCL_OK) { + fprintf(stderr, "Failed to report processor status\n"); + fprintf(stderr, "\tStage %s, status '%s'\n", + id, txt); + fprintf(stderr, "\tError Message was '%s'\n", sim_interp->result); + } +} + +/* + * Tcl_AppInit - Called by TCL to perform application-specific initialization. + */ +int Tcl_AppInit(Tcl_Interp *interp) +{ + /* Tell TCL about the name of the simulator so it can */ + /* use it as the title of the main window */ + Tcl_SetVar(interp, "simname", simname, TCL_GLOBAL_ONLY); + + if (Tcl_Init(interp) == TCL_ERROR) + return TCL_ERROR; + if (Tk_Init(interp) == TCL_ERROR) + return TCL_ERROR; + Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit); + + /* Call procedure to add new commands */ + addAppCommands(interp); + + /* + * Specify a user-specific startup file to invoke if the application + * is run interactively. Typically the startup file is "~/.apprc" + * where "app" is the name of the application. If this line is deleted + * then no user-specific startup file will be run under any conditions. + */ + Tcl_SetVar(interp, "tcl_rcFileName", "~/.wishrc", TCL_GLOBAL_ONLY); + return TCL_OK; + +} + + +#endif /* HAS_GUI */ diff --git a/arch/sim/y86-code/Makefile b/arch/sim/y86-code/Makefile new file mode 100644 index 0000000..12bc0ae --- /dev/null +++ b/arch/sim/y86-code/Makefile @@ -0,0 +1,54 @@ +CC=gcc +CFLAGS=-Wall -O2 + +ISADIR = ../misc +YAS=$(ISADIR)/yas +YIS=$(ISADIR)/yis +PIPE=../pipe/psim +SEQ=../seq/ssim +SEQ+ =../seq/ssim+ + +YOFILES = abs-asum-cmov.yo abs-asum-jmp.yo asum.yo asumr.yo asumi.yo cjr.yo j-cc.yo poptest.yo pushquestion.yo pushtest.yo prog1.yo prog2.yo prog3.yo prog4.yo prog5.yo prog6.yo prog7.yo prog8.yo prog9.yo prog10.yo ret-hazard.yo + +PIPEFILES = asum.pipe asumr.pipe cjr.pipe j-cc.pipe poptest.pipe pushquestion.pipe pushtest.pipe prog1.pipe prog2.pipe prog3.pipe prog4.pipe prog5.pipe prog6.pipe prog7.pipe prog8.pipe ret-hazard.pipe + +SEQFILES = asum.seq asumr.seq cjr.seq j-cc.seq poptest.seq pushquestion.seq pushtest.seq prog1.seq prog2.seq prog3.seq prog4.seq prog5.seq prog6.seq prog7.seq prog8.seq ret-hazard.seq + +SEQ+FILES = asum.seq+ asumr.seq+ cjr.seq+ j-cc.seq+ poptest.seq+ pushquestion.seq+ pushtest.seq+ prog1.seq+ prog2.seq+ prog3.seq+ prog4.seq+ prog5.seq+ prog6.seq+ prog7.seq+ prog8.seq+ ret-hazard.seq+ + +.SUFFIXES: +.SUFFIXES: .c .s .o .ys .yo .yis .pipe .seq .seq+ + +all: $(YOFILES) + +test: testpsim testssim testssim+ + +testpsim: $(PIPEFILES) + grep "ISA Check" *.pipe + rm $(PIPEFILES) + +testssim: $(SEQFILES) + grep "ISA Check" *.seq + rm $(SEQFILES) + +testssim+: $(SEQ+FILES) + grep "ISA Check" *.seq+ + rm $(SEQ+FILES) + +.ys.yo: + $(YAS) $*.ys + +.yo.yis: $(YIS) + $(YIS) $*.yo > $*.yis + +.yo.pipe: $(PIPE) + $(PIPE) -t $*.yo > $*.pipe + +.yo.seq: $(SEQ) + $(SEQ) -t $*.yo > $*.seq + +.yo.seq+: $(SEQ+) + $(SEQ+) -t $*.yo > $*.seq+ + +clean: + rm -f *.o *.yis *~ *.yo *.pipe *.seq *.seq+ core diff --git a/arch/sim/y86-code/README b/arch/sim/y86-code/README new file mode 100644 index 0000000..e1f27fe --- /dev/null +++ b/arch/sim/y86-code/README @@ -0,0 +1,20 @@ +This directory contains examples of Y86-64 assembly code programs +(extension `.ys') used in Chapter 4 of CS:APP3e. + +Given an assembly code file "file.ys", you can assemble it with the +command "make file.yo". The resulting file is in the "object code" +format described in the book. + +You can assemble and simulate all the test programs in this directory. +First, you need to make the different simulators in the pipe (psim) +and seq (ssim and ssim+) directories. Then use the following +commands: + +PIPE: make testpsim +SEQ: make testssim +SEQ+: make testssim+ + +Each of these commands will cause a number of programs to be assembled +and simulated. Lots of things will scroll by, but you should see the message +"ISA Check Succeeds" for each of the programs tested. + diff --git a/arch/sim/y86-code/abs-asum-cmov.ys b/arch/sim/y86-code/abs-asum-cmov.ys new file mode 100644 index 0000000..de5ec7f --- /dev/null +++ b/arch/sim/y86-code/abs-asum-cmov.ys @@ -0,0 +1,46 @@ +# Modification of asum code to compute absolute values of entries. +# This version uses a conditional move +# Execution begins at address 0 + .pos 0 + irmovq stack, %rsp # Set up stack pointer + call main # Execute main program + halt # Terminate program + +# Array of 4 elements + .align 8 +array: .quad 0x0000000d000d000d + .quad 0xffffff3fff3fff40 # -0x000000c000c000c0 + .quad 0x00000b000b000b00 + .quad 0xffff5fff5fff6000 # -0x0000a000a000a000 + +main: + irmovq array,%rdi + irmovq $4,%rsi + call absSum # absSum(array, 4) + ret + +# long absSum(long *start, long count) +# start in %rdi, count in %rsi +absSum: + irmovq $8,%r8 # Constant 8 + irmovq $1,%r9 # Constant 1 + xorq %rax,%rax # sum = 0 + andq %rsi,%rsi # Set condition codes + jmp test +/* $begin abs-sum-cmov-ys */ +loop: + mrmovq (%rdi),%r10 # x = *start + xorq %r11,%r11 # Constant 0 + subq %r10,%r11 # -x + cmovg %r11,%r10 # If -x > 0 then x = -x + addq %r10,%rax # Add to sum + addq %r8,%rdi # start++ + subq %r9,%rsi # count-- +test: + jne loop # Stop when 0 +/* $end abs-sum-cmov-ys */ + ret + +# The stack starts here and grows to lower addresses + .pos 0x200 +stack: diff --git a/arch/sim/y86-code/abs-asum-jmp.ys b/arch/sim/y86-code/abs-asum-jmp.ys new file mode 100644 index 0000000..2951bfb --- /dev/null +++ b/arch/sim/y86-code/abs-asum-jmp.ys @@ -0,0 +1,46 @@ +# Modification of asum code to compute absolute values of entries. +# This version uses a conditional jump +# Execution begins at address 0 + .pos 0 + irmovq stack, %rsp # Set up stack pointer + call main # Execute main program + halt # Terminate program + +# Array of 4 elements + .align 8 +array: .quad 0x0000000d000d000d + .quad 0xffffff3fff3fff40 # -0x000000c000c000c0 + .quad 0x00000b000b000b00 + .quad 0xffff5fff5fff6000 # -0x0000a000a000a000 + +main: irmovq array,%rdi + irmovq $4,%rsi + call absSum # absSum(array, 4) + ret +/* $begin abs-sum-jmp-ys */ +# long absSum(long *start, long count) +# start in %rdi, count in %rsi +absSum: + irmovq $8,%r8 # Constant 8 + irmovq $1,%r9 # Constant 1 + xorq %rax,%rax # sum = 0 + andq %rsi,%rsi # Set condition codes + jmp test +loop: + mrmovq (%rdi),%r10 # x = *start + xorq %r11,%r11 # Constant 0 + subq %r10,%r11 # -x + jle pos # Skip if -x <= 0 + rrmovq %r11,%r10 # x = -x +pos: + addq %r10,%rax # Add to sum + addq %r8,%rdi # start++ + subq %r9,%rsi # count-- +test: + jne loop # Stop when 0 + ret +/* $end abs-sum-jmp-ys */ + +# The stack starts here and grows to lower addresses + .pos 0x200 +stack: diff --git a/arch/sim/y86-code/asum.ys b/arch/sim/y86-code/asum.ys new file mode 100644 index 0000000..2c74f34 --- /dev/null +++ b/arch/sim/y86-code/asum.ys @@ -0,0 +1,35 @@ +# Execution begins at address 0 + .pos 0 + irmovq stack, %rsp # Set up stack pointer + call main # Execute main program + halt # Terminate program + +# Array of 4 elements + .align 8 +array: .quad 0x000d000d000d + .quad 0x00c000c000c0 + .quad 0x0b000b000b00 + .quad 0xa000a000a000 + +main: irmovq array,%rdi + irmovq $4,%rsi + call sum # sum(array, 4) + ret + +# long sum(long *start, long count) +# start in %rdi, count in %rsi +sum: irmovq $8,%r8 # Constant 8 + irmovq $1,%r9 # Constant 1 + xorq %rax,%rax # sum = 0 + andq %rsi,%rsi # Set CC + jmp test # Goto test +loop: mrmovq (%rdi),%r10 # Get *start + addq %r10,%rax # Add to sum + addq %r8,%rdi # start++ + subq %r9,%rsi # count--. Set CC +test: jne loop # Stop when 0 + ret # Return + +# Stack starts here and grows to lower addresses + .pos 0x200 +stack: diff --git a/arch/sim/y86-code/asumi.ys b/arch/sim/y86-code/asumi.ys new file mode 100644 index 0000000..5821d06 --- /dev/null +++ b/arch/sim/y86-code/asumi.ys @@ -0,0 +1,38 @@ +# Execution begins at address 0 + .pos 0 + irmovq stack, %rsp # Set up stack pointer + call main # Execute main program + halt # Terminate program + +# Array of 4 elements + .align 8 +array: .quad 0x000d000d000d + .quad 0x00c000c000c0 + .quad 0x0b000b000b00 + .quad 0xa000a000a000 + +main: irmovq array,%rdi + irmovq $4,%rsi + call sum # sum(array, 4) + ret + +/* $begin sumi-ys */ +# long sum(long *start, long count) +# start in %rdi, count in %rsi +sum: + xorq %rax,%rax # sum = 0 + andq %rsi,%rsi # Set condition codes + jmp test +loop: + mrmovq (%rdi),%r10 # Get *start + addq %r10,%rax # Add to sum + iaddq $8,%rdi # start++ + iaddq $-1,%rsi # count-- +test: + jne loop # Stop when 0 + ret +/* $end sumi-ys */ + +# The stack starts here and grows to lower addresses + .pos 0x100 +stack: diff --git a/arch/sim/y86-code/asumr.ys b/arch/sim/y86-code/asumr.ys new file mode 100644 index 0000000..7091271 --- /dev/null +++ b/arch/sim/y86-code/asumr.ys @@ -0,0 +1,42 @@ +# Execution begins at address 0 + .pos 0 + irmovq stack, %rsp # Set up stack pointer + call main # Execute main program + halt # Terminate program + +# Array of 4 elements + .align 8 +array: .quad 0x000d000d000d + .quad 0x00c000c000c0 + .quad 0x0b000b000b00 + .quad 0xa000a000a000 + +main: irmovq array,%rdi + irmovq $4,%rsi + call rsum # rsum(array, 4) + ret + +/* $begin rsum-ys */ +# long rsum(long *start, long count) +# start in %rdi, count in %rsi +rsum: + xorq %rax,%rax # Set return value to 0 + andq %rsi,%rsi # Set condition codes + jle return # If count <= 0, return 0 + pushq %rbx # Save callee-saved register + mrmovq (%rdi),%rbx # Get *start + irmovq $-1,%r10 + addq %r10,%rsi # count-- + irmovq $8,%r10 + addq %r10,%rdi # start++ + call rsum + addq %rbx,%rax # Add *start to sum + popq %rbx # Restore callee-saved register +return: + ret +/* $end rsum-ys */ + +# The stack starts here and grows to lower addresses + .pos 0x200 +stack: + diff --git a/arch/sim/y86-code/cjr.ys b/arch/sim/y86-code/cjr.ys new file mode 100644 index 0000000..a883b16 --- /dev/null +++ b/arch/sim/y86-code/cjr.ys @@ -0,0 +1,17 @@ +# /* $begin cjr-ys */ +# Code to generate a combination of not-taken branch and ret + irmovq Stack, %rsp + irmovq rtnp,%rax + pushq %rax # Set up return pointer + xorq %rax,%rax # Set Z condition code + jne target # Not taken (First part of combination) + irmovq $1,%rax # Should execute this + halt +target: ret # Second part of combination + irmovq $2,%rbx # Should not execute this + halt +rtnp: irmovq $3,%rdx # Should not execute this + halt +.pos 0x40 +Stack: +# /* $end cjr-ys */ diff --git a/arch/sim/y86-code/j-cc.ys b/arch/sim/y86-code/j-cc.ys new file mode 100644 index 0000000..e8a6eb3 --- /dev/null +++ b/arch/sim/y86-code/j-cc.ys @@ -0,0 +1,15 @@ + irmovq $1, %rsi + irmovq $2, %rdi + irmovq $4, %rbp + irmovq $-32, %rax + irmovq $64, %rdx + subq %rdx,%rax + je target + nop + halt +target: + addq %rsi,%rdx + nop + nop + nop + halt diff --git a/arch/sim/y86-code/poptest.ys b/arch/sim/y86-code/poptest.ys new file mode 100644 index 0000000..236880d --- /dev/null +++ b/arch/sim/y86-code/poptest.ys @@ -0,0 +1,6 @@ +# Test of Pop semantics for Y86-64 + irmovq $0x100,%rsp # Initialize stack pointer + irmovq $0xABCD,%rax + pushq %rax # Put known value on stack + popq %rsp # Either get 0xABCD, or 0xfc + halt diff --git a/arch/sim/y86-code/prog1.ys b/arch/sim/y86-code/prog1.ys new file mode 100644 index 0000000..f2f4dc5 --- /dev/null +++ b/arch/sim/y86-code/prog1.ys @@ -0,0 +1,8 @@ +# prog1: Pad with 3 nop's + irmovq $10,%rdx + irmovq $3,%rax + nop + nop + nop + addq %rdx,%rax + halt diff --git a/arch/sim/y86-code/prog10.ys b/arch/sim/y86-code/prog10.ys new file mode 100644 index 0000000..a5dc8c7 --- /dev/null +++ b/arch/sim/y86-code/prog10.ys @@ -0,0 +1,7 @@ +# prog10 + irmovq $1,%rax + xorq %rsp,%rsp # Set stack pointer to 0 and CC to 100 + pushq %rax # Attempt to write to 0xfffffffffffffff8 + addq %rax,%rax # (Should not be executed) Would set CC to 000 + irmovq $2, %rax # Not executed + irmovq $3, %rax # Not executed diff --git a/arch/sim/y86-code/prog2.ys b/arch/sim/y86-code/prog2.ys new file mode 100644 index 0000000..76634f0 --- /dev/null +++ b/arch/sim/y86-code/prog2.ys @@ -0,0 +1,7 @@ +# prog2: Pad with 2 nop's + irmovq $10,%rdx + irmovq $3,%rax + nop + nop + addq %rdx,%rax + halt diff --git a/arch/sim/y86-code/prog3.ys b/arch/sim/y86-code/prog3.ys new file mode 100644 index 0000000..08c9fe1 --- /dev/null +++ b/arch/sim/y86-code/prog3.ys @@ -0,0 +1,6 @@ +# prog3: Pad with 1 nop + irmovq $10,%rdx + irmovq $3,%rax + nop + addq %rdx,%rax + halt diff --git a/arch/sim/y86-code/prog4.ys b/arch/sim/y86-code/prog4.ys new file mode 100644 index 0000000..d9ca4ad --- /dev/null +++ b/arch/sim/y86-code/prog4.ys @@ -0,0 +1,5 @@ +# prog4: No padding + irmovq $10,%rdx + irmovq $3,%rax + addq %rdx,%rax + halt diff --git a/arch/sim/y86-code/prog5.ys b/arch/sim/y86-code/prog5.ys new file mode 100644 index 0000000..3a9193c --- /dev/null +++ b/arch/sim/y86-code/prog5.ys @@ -0,0 +1,8 @@ +# prog5: Load/use hazard + irmovq $128,%rdx + irmovq $3,%rcx + rmmovq %rcx, 0(%rdx) + irmovq $10,%rbx + mrmovq 0(%rdx), %rax # Load %rax + addq %rbx,%rax # Use %rax + halt diff --git a/arch/sim/y86-code/prog6.ys b/arch/sim/y86-code/prog6.ys new file mode 100644 index 0000000..c9db5bb --- /dev/null +++ b/arch/sim/y86-code/prog6.ys @@ -0,0 +1,14 @@ +# Demonstration of return +# /* $begin prog6-ys */ +# prog6 + irmovq stack,%rsp # Initialize stack pointer + call proc # Procedure call + irmovq $10,%rdx # Return point + halt +.pos 0x20 +proc: # proc: + ret # Return immediately + rrmovq %rdx,%rbx # Not executed +.pos 0x30 +stack: # stack: Stack pointer +# /* $end prog6-ys */ diff --git a/arch/sim/y86-code/prog7.ys b/arch/sim/y86-code/prog7.ys new file mode 100644 index 0000000..01702b3 --- /dev/null +++ b/arch/sim/y86-code/prog7.ys @@ -0,0 +1,13 @@ +# Demonstrate branch cancellation +# /* $begin prog7-ys */ +# prog7 + xorq %rax,%rax + jne target # Not taken + irmovq $1, %rax # Fall through + halt +target: + irmovq $2, %rdx # Target + irmovq $3, %rbx # Target+1 +# /* $end prog7-ys */ + halt + diff --git a/arch/sim/y86-code/prog8.ys b/arch/sim/y86-code/prog8.ys new file mode 100644 index 0000000..817518c --- /dev/null +++ b/arch/sim/y86-code/prog8.ys @@ -0,0 +1,5 @@ +# prog8: Forwarding Priority + irmovq $10,%rdx + irmovq $3,%rdx + rrmovq %rdx,%rax + halt diff --git a/arch/sim/y86-code/prog9.ys b/arch/sim/y86-code/prog9.ys new file mode 100644 index 0000000..2dd364f --- /dev/null +++ b/arch/sim/y86-code/prog9.ys @@ -0,0 +1,9 @@ +# Exception handling +# /* $begin prog9-yo */ + xorq %rax,%rax + jne target # Not taken + irmovq $1, %rax # Fall through + halt +target: + .byte 0xFF # Invalid instruction code +# /* $end prog9-yo */ diff --git a/arch/sim/y86-code/pushquestion.ys b/arch/sim/y86-code/pushquestion.ys new file mode 100644 index 0000000..a055632 --- /dev/null +++ b/arch/sim/y86-code/pushquestion.ys @@ -0,0 +1,5 @@ + # Assembly Code to test semantics of pushq + irmovq 0x100, %rsp + pushq %rsp # Ambiguous + popq %rax + halt diff --git a/arch/sim/y86-code/pushtest.ys b/arch/sim/y86-code/pushtest.ys new file mode 100644 index 0000000..bdb5e63 --- /dev/null +++ b/arch/sim/y86-code/pushtest.ys @@ -0,0 +1,7 @@ +# Test of Push semantics for Y86-64 + irmovq $0x100,%rsp # Initialize stack pointer + rrmovq %rsp,%rax # Save stack pointer + pushq %rsp # Push the stack pointer (old or new?) + popq %rdx # Get it back + subq %rdx,%rax # Compute difference. Either 0 (old) or 4 (new). + halt diff --git a/arch/sim/y86-code/ret-hazard.ys b/arch/sim/y86-code/ret-hazard.ys new file mode 100644 index 0000000..51add37 --- /dev/null +++ b/arch/sim/y86-code/ret-hazard.ys @@ -0,0 +1,13 @@ +/* $begin ret-hazard-ys */ +# Test instruction that modifies %esp followed by ret + irmovq mem,%rbx + mrmovq 0(%rbx),%rsp # Sets %rsp to point to return point + ret # Returns to return point + halt # +rtnpt: irmovq $5,%rsi # Return point + halt +.pos 0x40 +mem: .quad stack # Holds desired stack pointer +.pos 0x50 +stack: .quad rtnpt # Top of stack: Holds return point +/* $end ret-hazard-ys */ diff --git a/arch/simguide.pdf b/arch/simguide.pdf new file mode 100644 index 0000000..280d15c Binary files /dev/null and b/arch/simguide.pdf differ