Bird Instruction Format
Format for new instructions of Bird. (as discussed in the lecture)
Machine Code Format PUSH
Push r
Machine Code Format POP
POP r
Machine Code Format CALL
CALL x
Machine Code Format RET
RET
Main Module
//Main module module main_module ( output wire [3:0] rowwrite, input [3:0] colread, input clk, output wire [3:0] grounds, output wire [6:0] display, input pushbutton //may be used as clock ); reg [15:0] data_all; wire [3:0] keyout; reg [25:0] clk1; reg [1:0] ready_buffer; reg ack; reg statusordata; //memory map is defined here localparam BEGINMEM=12'h000, ENDMEM=12'h1ff, KEYPAD=12'h900, SEVENSEG=12'hb00; // memory chip reg [15:0] memory [0:127]; // cpu's input-output pins wire [15:0] data_out; reg [15:0] data_in; wire [11:0] address; wire memwt; sevensegment ss1 (//to be added); keypad kp1(//to be added); bird br1 (//to be added); //multiplexer for cpu input always @* if ( (BEGINMEM<=address) && (address<=ENDMEM) ) begin data_in=memory[address]; ack=0; statusordata=0; end else if (address==KEYPAD+1) begin statusordata=1; data_in=keyout; ack=0; end else if (address==KEYPAD) begin //to be added end else begin data_in=16'hf345; //any number ack=0; statusordata=0; end //multiplexer for cpu output always @(posedge clk) //data output port of the cpu if (memwt) if ( (BEGINMEM<=address) && (address<=ENDMEM) ) memory[address]<=data_out; else if ( SEVENSEG==address) data_all<=data_out; initial begin data_all=0; ack=0; statusordata=0; $readmemh("ram.dat", memory); end endmodule
CPU Module
//bird CPU module bird ( input clk, input [15:0] data_in, output reg [15:0] data_out, output reg [11:0] address, output memwt ); reg [11:0] pc, ir; //program counter, instruction register reg [4:0] state; //FSM reg [15:0] regbank [7:0];//registers reg zeroflag; //zero flag register reg [15:0] result; //output for result localparam FETCH=4'b0000, LDI=4'b0001, LD=4'b0010, ST=4'b0011, JZ=4'b0100, JMP=4'b0101, ALU=4'b0111, PUSH=4'b1000, POP1=4'b1001, POP2=4'b1100, CALL=4'b1010, RET1=4'b1011, RET2=4'b1101; wire zeroresult; always @(posedge clk) case(state) FETCH: begin if ( data_in[15:12]==JZ) // if instruction is jz if (zeroflag) //and if last bit of 7th register is 0 then jump to jump instruction state state <= JMP; else state <= FETCH; //stay here to catch next instruction else state <= data_in[15:12]; //read instruction opcode and jump the state of the instruction to be read ir<=data_in[11:0]; //read instruction details into instruction register pc<=pc+1; //increment program counter end LDI: begin regbank[ ir[2:0] ] <= data_in; //if inst is LDI get the destination register number from ir and move the data in it. pc<=pc+1; //for next instruction (32 bit instruction) state <= FETCH; end LD: begin regbank[ir[2:0]] <= data_in; state <= FETCH; end ST: begin state <= FETCH; end JMP: begin pc <= pc+ir; state <= FETCH; end ALU: begin regbank[ir[2:0]]<=result; zeroflag<=zeroresult; state <= FETCH; end PUSH: begin regbank[7]<=regbank[7]-1; state <= FETCH; end POP1: begin //to be added end POP2: begin //to be added end CALL: begin //to be added end RET1: begin //to be added end RET2: begin //to be added end endcase always @* case (state) LD: address=regbank[ir[5:3]][11:0]; ST: address=regbank[ir[5:3]][11:0]; PUSH: address=regbank[7]; POP2: //to be added CALL: //to be added RET2: //to be added default: address=pc; endcase assign memwt=(state==ST)|| // to be added always @* case (state) CALL: data_out = //to be added default: data_out = regbank[ir[8:6]]; endcase always @* //ALU Operation case (ir[11:9]) 3'h0: result = regbank[ir[8:6]]+regbank[ir[5:3]]; //000 3'h1: result = regbank[ir[8:6]]-regbank[ir[5:3]]; //001 3'h2: result = regbank[ir[8:6]]®bank[ir[5:3]]; //010 3'h3: result = regbank[ir[8:6]]|regbank[ir[5:3]]; //011 3'h4: result = regbank[ir[8:6]]^regbank[ir[5:3]]; //100 3'h7: case (ir[8:6]) 3'h0: result = !regbank[ir[5:3]]; 3'h1: result = regbank[ir[5:3]]; 3'h2: result = regbank[ir[5:3]]+1; 3'h3: result = regbank[ir[5:3]]-1; default: result=16'h0000; endcase default: result=16'h0000; endcase assign zeroresult = ~|result; initial begin; state=FETCH; zeroflag=0; pc=0; end endmodule
Keypad Module
module keypad ( output reg [3:0] rowwrite, input [3:0] colread, input clk, input ack, input statusordata, output reg [15:0] keyout ); wire keypressed; reg [25:0] clk1; reg ready; reg [3:0] keyread, data; reg [3:0] rowpressed; reg [3:0] pressedcol [0:3]; reg [11:0] rowpressed_buffer0, rowpressed_buffer1, rowpressed_buffer2, rowpressed_buffer3; reg [3:0] rowpressed_debounced; always @(posedge clk) clk1<=clk1+1; always @(posedge clk1[15]) rowwrite<={rowwrite[2:0], rowwrite[3]}; always @(posedge clk1[15]) if (rowwrite== 4'b1110) begin rowpressed[0]<= ~(&colread); //colread=1111--> none of them pressed, colread=1110 --> 1, colread=1101-->2, 1011-->3, 0111->A pressedcol[0]<=colread; end else if (rowwrite==4'b1101) begin rowpressed[1]<= ~(&colread); //colread=1111--> none of them pressed, colread=1110 --> 4, colread=1101-->5, 1011-->6, 0111->B pressedcol[1]<=colread; end else if (rowwrite==4'b1011) begin rowpressed[2]<= ~(&colread); //colread=1111--> none of them pressed, colread=1110 --> 7, colread=1101-->8, 1011-->9, 0111->C pressedcol[2]<=colread; end else if (rowwrite==4'b0111) begin rowpressed[3]<= ~(&colread); //colread=1111--> none of them pressed, colread=1110 --> *(E), colread=1101-->0, 1011-->#(F), 0111->D pressedcol[3]<=colread; end wire transition0_10; wire transition0_01; assign transition0_10=~|rowpressed_buffer0; assign transition0_01=&rowpressed_buffer0; wire transition1_10; wire transition1_01; assign transition1_10=~|rowpressed_buffer1; assign transition1_01=&rowpressed_buffer1; wire transition2_10; wire transition2_01; assign transition2_10=~|rowpressed_buffer2; assign transition2_01=&rowpressed_buffer2; wire transition3_10; wire transition3_01; assign transition3_10=~|rowpressed_buffer3; //kpd=1-->0 assign transition3_01=&rowpressed_buffer3; //kpd=0-->1 always @(posedge clk1[15]) begin rowpressed_buffer0<= {rowpressed_buffer0[10:0],rowpressed[0]}; if (rowpressed_debounced[0]==0 && transition0_01) rowpressed_debounced[0]<=1; if (rowpressed_debounced[0]==1 && transition0_10) rowpressed_debounced[0]<=0; rowpressed_buffer1<= {rowpressed_buffer1[10:0],rowpressed[1]}; if (rowpressed_debounced[1]==0 && transition1_01) rowpressed_debounced[1]<=1; if (rowpressed_debounced[1]==1 && transition1_10) rowpressed_debounced[1]<=0; rowpressed_buffer2<= {rowpressed_buffer2[10:0],rowpressed[2]}; if (rowpressed_debounced[2]==0 && transition2_01) rowpressed_debounced[2]<=1; if (rowpressed_debounced[2]==1 && transition2_10) rowpressed_debounced[2]<=0; rowpressed_buffer3<= {rowpressed_buffer3[10:0],rowpressed[3]}; if (rowpressed_debounced[3]==0 && transition3_01) rowpressed_debounced[3]<=1; if (rowpressed_debounced[3]==1 && transition3_10) rowpressed_debounced[3]<=0; end always @* begin if (rowpressed_debounced[0]==1) begin if (pressedcol[0]==4'b1110) keyread=4'h1; else if (pressedcol[0]==4'b1101) keyread=4'h2; else if (pressedcol[0]==4'b1011) keyread=4'h3; else if (pressedcol[0]==4'b0111) keyread=4'hA; else keyread=4'b0000; end else if (rowpressed_debounced[1]==1) begin if (pressedcol[1]==4'b1110) keyread=4'h4; else if (pressedcol[1]==4'b1101) keyread=4'h5; else if (pressedcol[1]==4'b1011) keyread=4'h6; else if (pressedcol[1]==4'b0111) keyread=4'hB; else keyread=4'b0000; end else if (rowpressed_debounced[2]==1) begin if (pressedcol[2]==4'b1110) keyread=4'h7; else if (pressedcol[2]==4'b1101) keyread=4'h8; else if (pressedcol[2]==4'b1011) keyread=4'h9; else if (pressedcol[2]==4'b0111) keyread=4'hC; else keyread=4'b0000; end else if (rowpressed_debounced[3]==1) begin if (pressedcol[3]==4'b1110) keyread=4'hE; else if (pressedcol[3]==4'b1101) keyread=4'h0; else if (pressedcol[3]==4'b1011) keyread=4'hF; else if (pressedcol[3]==4'b0111) keyread=4'hD; else keyread=4'b0000; end else keyread=4'b0000; end //always assign keypressed= rowpressed_debounced[0]||rowpressed_debounced[1]||rowpressed_debounced[2]||rowpressed_debounced[3]; reg [1:0] keypressed_buffer; //yeni karakter için parmağı çekip tekrar basmamız için gerekli always @(posedge clk) keypressed_buffer<={keypressed_buffer[0],keypressed}; always @(posedge clk) if ((keypressed_buffer==2'b01)&&(ready==0)) begin data<=keyread; ready<=1; end else if ((ack==1)&&(ready==1)) ready<=0; always @(*) if (statusordata==1) keyout={15'b0,ready}; else keyout={12'b0,data}; initial begin rowwrite=4'b1110; ready=0; end endmodule
Seven Segment Display Module
module sevensegment(datain, grounds, display, clk); input wire [15:0] datain; output reg [3:0] grounds; output reg [6:0] display; input clk; reg [3:0] data [3:0]; reg [1:0] count; reg [25:0] clk1; always @(posedge clk1[15]) begin grounds <= {grounds[2:0],grounds[3]}; count <= count + 1; end always @(posedge clk) clk1 <= clk1 + 1; always @(*) case(data[count]) 0:display=7'b1111110; //starts with a, ends with g 1:display=7'b0110000; 2:display=7'b1101101; 3:display=7'b1111001; 4:display=7'b0110011; 5:display=7'b1011011; 6:display=7'b1011111; 7:display=7'b1110000; 8:display=7'b1111111; 9:display=7'b1111011; 'ha:display=7'b1110111; 'hb:display=7'b0011111; 'hc:display=7'b1001110; 'hd:display=7'b0111101; 'he:display=7'b1001111; 'hf:display=7'b1000111; default display=7'b1111111; endcase always @* begin data[0] = datain[15:12]; data[1] = datain[11:8]; data[2] = datain[7:4]; data[3] = datain[3:0]; end initial begin count = 2'b0; grounds = 4'b1110; clk1 = 0; end endmodule
Bird Assembler
// to compile, gcc assembler.c -o assembler // No error check is provided. // Variable names cannot start with numeric characters, ie, with 0-9. // hexadecimals are twos complement. // first address of the code section is zero, and the data section follows the code section in memory. // four tables are formed: jump table, ldi table, label table and variable table. #include <stdio.h> #include <stdlib.h> #include <string.h> //Converts a hexadecimal string to integer. int hex2int( char* hex) { int result=0; while ((*hex)!='\0') { if (('0'<=(*hex))&&((*hex)<='9')) result = result*16 + (*hex) -'0'; else if (('a'<=(*hex))&&((*hex)<='f')) result = result*16 + (*hex) -'a'+10; else if (('A'<=(*hex))&&((*hex)<='F')) result = result*16 + (*hex) -'A'+10; hex++; } return(result); } main() { FILE *fp; char line[100]; char *token = NULL; char *op1, *op2, *op3, *label; char ch; int chch; int program[1000]; int counter=0; //holds the address of the machine code instruction struct label_or_variable { int location; char *name; }; // A label is a symbol which mark a location within the code section. In the example // program above, the strings "lpp", "loop" and "lp1" are labels. // In reptile, labels are used by jump, jz and ldi instructions. struct label_or_variable labeltable[50]; //there can be 50 labels at most in our programs int nooflabels = 0; //number of labels encountered during assembly. // A variable is a symbol which mark a location within the data section. In the example // program above, the strings "", "" and "" are variables. // In reptile, variables are used by ldi instructions. struct label_or_variable variabletable[50]; // The list of variables in .data section and their locations. int noofvariables = 0; //number of jumps encountered during assembly. // Jump instructions cannot be assembled readily because we may not know the value of // the label when we encountered a jump instruction. This happens if the label used by // that jump instruction appear below that jump instruction. This is the situation // with the label "loop" in the example program above. Hence, the location of jump // instructions must be stored. struct label_or_variable jumptable[100]; //There can be at most 100 jumps int noofjumps=0; //number of jump instructions encountered during assembly. //Variables and labels are used by ldi instructions. //The memory for the variables are traditionally allocated at the end of the code section. //Hence their addresses are not known when we assemble a ldi instruction. Also, the value of //a label may not be known when we encounter a ldi instruction which uses that label. //Hence, the location of the ldi instructions must be kept, and these instructions must be //modified when we discover the address of the label or variable that it uses. struct label_or_variable lditable[100]; int noofldis=0; fp = fopen("code.txt","r"); if (fp != NULL) { while(fgets(line,sizeof line,fp)!= NULL) //skip till .code section { token=strtok(line,"\n\t\r "); if (strcmp(token,".code")==0 ) break; } while(fgets(line,sizeof line,fp)!= NULL) { token=strtok(line,"\n\t\r "); //get the instruction mnemonic or label //======================================== FIRST PASS ====================================================== while (token) { if (strcmp(token,"ldi")==0) //---------------LDI INSTRUCTION-------------------- { op1 = strtok(NULL,"\n\t\r "); //get the 1st operand of ldi, which is the register that ldi loads op2 = strtok(NULL,"\n\t\r "); //get the 2nd operand of ldi, which is the data that is to be loaded program[counter]=0x1000+hex2int(op1); //generate the first 16-bit of the ldi instruction counter++; //move to the second 16-bit of the ldi instruction if ((op2[0]=='0')&&(op2[1]=='x')) //if the 2nd operand is twos complement hexadecimal program[counter]=hex2int(op2+2)&0xffff; //convert it to integer and form the second 16-bit else if (( (op2[0])=='-') || ((op2[0]>='0')&&(op2[0]<='9'))) //if the 2nd operand is decimal program[counter]=atoi(op2)&0xffff; //convert it to integer and form the second 16-bit else //if the second operand is not decimal or hexadecimal, it is a laber or a variable. { //in this case, the 2nd 16-bits of the ldi instruction cannot be generated. lditable[noofldis].location = counter; //record the location of this 2nd 16-bit op1=(char*)malloc(sizeof(op2)); //and the name of the label/variable that it must contain strcpy(op1,op2); //in the lditable array. lditable[noofldis].name = op1; noofldis++; } counter++; //skip to the next memory location } else if (strcmp(token,"ld")==0) //------------LD INSTRUCTION--------------------- { op1 = strtok(NULL,"\n\t\r "); //get the 1st operand of ld, which is the destination register op2 = strtok(NULL,"\n\t\r "); //get the 2nd operand of ld, which is the source register ch = (op1[0]-48)| ((op2[0]-48) << 3); //form bits 11-0 of machine code. 48 is ASCII value of '0' program[counter]=0x2000+((ch)&0x00ff); //form the instruction and write it to memory counter++; //skip to the next empty location in memory } else if (strcmp(token,"st")==0) //-------------ST INSTRUCTION-------------------- { op1 = strtok(NULL,"\n\t\r "); //get the 1st operand of ld, which is the destination register op2 = strtok(NULL,"\n\t\r "); //get the 2nd operand of ld, which is the source register chch = (op1[0]-48)<<3| ((op2[0]-48) << 6); //form bits 11-0 of machine code. 48 is ASCII value of '0' program[counter]=0x3000+((chch)&0x01ff); //form the instruction and write it to memory counter++; //skip to the next empty location in memory } else if (strcmp(token,"jz")==0) //------------- CONDITIONAL JUMP ------------------ { op1 = strtok(NULL,"\n\t\r "); //read the label string jumptable[noofjumps].location = counter; //write the jz instruction's location into the jumptable op2=(char*)malloc(sizeof(op1)); //allocate space for the label strcpy(op2,op1); //copy the label into the allocated space jumptable[noofjumps].name=op2; //point to the label from the jumptable noofjumps++; //skip to the next empty location in jumptable program[counter]=0x4000; //write the incomplete instruction (just opcode) to memory counter++; } else if (strcmp(token,"jmp")==0) //-------------- JUMP ----------------------------- { op1 = strtok(NULL,"\n\t\r "); //read the label string jumptable[noofjumps].location = counter; //write the jz instruction's location into the jumptable op2=(char*)malloc(sizeof(op1)); //allocate space for the label strcpy(op2,op1); //copy the label into the allocated space jumptable[noofjumps].name=op2; //point to the label from the jumptable noofjumps++; //skip to the next empty location in jumptable program[counter]=0x5000; //write the incomplete instruction (just opcode) to memory counter++; //skip to the next empty location in memory. } else if (strcmp(token,"add")==0) //----------------- ADD ------------------------------- { op1 = strtok(NULL,"\n\t\r "); op2 = strtok(NULL,"\n\t\r "); op3 = strtok(NULL,"\n\t\r "); chch = (op1[0]-48)| ((op2[0]-48)<<6)|((op3[0]-48)<<3); program[counter]=0x7000+((chch)&0x01ff); counter++; } else if (strcmp(token,"sub")==0) { //to be added } else if (strcmp(token,"and")==0) { //to be added } else if (strcmp(token,"or")==0) { //to be added } else if (strcmp(token,"xor")==0) { //to be added } else if (strcmp(token,"not")==0) { op1 = strtok(NULL,"\n\t\r "); op2 = strtok(NULL,"\n\t\r "); ch = (op1[0]-48)| ((op2[0]-48)<<3); program[counter]=0x7E00+((ch)&0x00ff); counter++; } else if (strcmp(token,"mov")==0) { //to be added } else if (strcmp(token,"inc")==0) { op1 = strtok(NULL,"\n\t\r "); ch = (op1[0]-48)| ((op1[0]-48)<<3); program[counter]=0x7E80+((ch)&0x00ff); counter++; } else if (strcmp(token,"dec")==0) { //to be added } else if (strcmp(token,"push")==0) { //to be added } else if (strcmp(token,"pop")==0) { //to be added } else if (strcmp(token,"call")==0) //-------------- CALL ----------------------------- { //to be added } else if (strcmp(token,"ret")==0) { //to be added } else //------WHAT IS ENCOUNTERED IS NOT AN INSTRUCTION BUT A LABEL. UPDATE THE LABEL TABLE-------- { labeltable[nooflabels].location = counter; //read the label and update labeltable. op1=(char*)malloc(sizeof(token)); strcpy(op1,token); labeltable[nooflabels].name=op1; nooflabels++; } token = strtok(NULL,",\n\t\r "); // if what is read before is an instruction, this will be NULL //if what is read before is an label, this will be an opcode. } } //================================= SECOND PASS ============================== //supply the address fields of the jump and jz instructions by matching jumptable and labeltable int i,j; for (i=0; i<noofjumps;i++) //for all jump/jz instructions encountered { j=0; while ((j<nooflabels)&&( strcmp(jumptable[i].name , labeltable[j].name ) != 0 )) //if the label for this jump/jz does not match with the j++; // jth label in the labeltable, check the next label.. program[jumptable[i].location] +=(labeltable[j].location-jumptable[i].location-1)&0x0fff; //copy the jump address into memory. } //search for the start of the .data segment rewind(fp); while(fgets(line,sizeof line,fp)!= NULL) //skip till .data, if no .data, also ok. { token=strtok(line,"\n\t\r "); if (strcmp(token,".data")==0 ) break; } // process the .data segment and generate the variabletable[] array. int dataarea=0; while(fgets(line,sizeof line,fp)!= NULL) { token=strtok(line,"\n\t\r "); if (strcmp(token,".code")==0 ) //go till the .code segment break; else if (token[strlen(token)-1]==':') { token[strlen(token)-1]='\0'; //will not cause memory leak, as we do not do malloc variabletable[noofvariables].location=counter+dataarea; op1=(char*)malloc(sizeof(token)); strcpy(op1,token); variabletable[noofvariables].name=op1; token = strtok(NULL,",\n\t\r "); if (token==NULL) program[counter+dataarea]=0; else if (strcmp(token, ".space")==0) { token=strtok(NULL,"\n\t\r "); dataarea+=atoi(token); } else if((token[0]=='0')&&(token[1]=='x')) program[counter+dataarea]=hex2int(token+2)&0xffff; else if (( (token[0])=='-') || ('0'<=(token[0])&&(token[0]<='9')) ) program[counter+dataarea]=atoi(token)&0xffff; noofvariables++; dataarea++; } } // supply the address fields for the ldi instructions from the variable table for( i=0; i<noofldis;i++) { j=0; while ((j<noofvariables)&&( strcmp( lditable[i].name , variabletable[j].name)!=0 )) j++; if (j<noofvariables) program[lditable[i].location] = variabletable[j].location; } // supply the address fields for the ldi instructions from the label table for( i=0; i<noofldis;i++) { j=0; while ((j<nooflabels)&&( strcmp( lditable[i].name , labeltable[j].name)!=0 )) j++; if (j<nooflabels){ program[lditable[i].location] = (labeltable[j].location)&0x0fff; printf("%d %d %d\n", i, j, (labeltable[j].location)); } } //display the resulting tables printf("LABEL TABLE\n"); for (i=0;i<nooflabels;i++) printf("%d %s\n", labeltable[i].location, labeltable[i].name); printf("\n"); printf("JUMP TABLE\n"); for (i=0;i<noofjumps;i++) printf("%d %s\n", jumptable[i].location, jumptable[i].name); printf("\n"); printf("VARIABLE TABLE\n"); for (i=0;i<noofvariables;i++) printf("%d %s\n", variabletable[i].location, variabletable[i].name); printf("\n"); printf("LDI INSTRUCTIONS\n"); for (i=0;i<noofldis;i++) printf("%d %s\n", lditable[i].location, lditable[i].name); printf("\n"); fclose(fp); fp = fopen("RAM","w"); fprintf(fp,"v2.0 raw\n"); for (i=0;i<counter+dataarea;i++) fprintf(fp,"%04x\n",program[i]); fclose(fp); fp = fopen("ram.dat","w"); for (i=0;i<counter+dataarea;i++) fprintf(fp, "%04x\n", program[i]); } }