diff --git a/Makefile b/Makefile index 0f9cdf3..3de7bf8 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ SYSTEM_VVP=system/system.vvp BOOT_CODE=boot_code/brainfuck.txt GTKWSAVE=./gtkwave_savefile.gtkw +MICROCODE=system/ucode.hex include common.mk diff --git a/README.md b/README.md index b4b28a0..1cb3f16 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A CPU that aims to be binary compatible with the 8086 and with as many optimisat * [ ] Is pipelined * [ ] Is Out of Order * [ ] Is superscalar +* [ ] Has been successfully synthesized ### Simulating it To simulate this project you need Icarus Verilog, bin86, GNU make, xxd and the posix coreutils. diff --git a/common.mk b/common.mk index 2a61129..89ea108 100644 --- a/common.mk +++ b/common.mk @@ -34,13 +34,13 @@ disas: $(subst .txt,.disas,${BOOT_CODE}) # Running simulation %.lx2 %.memdump: %.txt ${SYSTEM_VVP} ${QUIET_VVP} - ${Q}vvp "${SYSTEM_VVP}" -lxt2 +BOOT_CODE="$<" +WAVEFORM="$(subst .txt,.lx2,$<)" +MEMDUMP="$(subst .txt,.memdumptxt,$<)" + ${Q}vvp "${SYSTEM_VVP}" -lxt2 +BOOT_CODE="$<" +WAVEFORM="$(subst .txt,.lx2,$<)" +MEMDUMP="$(subst .txt,.memdumptxt,$<)" +MICROCODE="${MICROCODE}" ${Q}grep -v '^//' "$(subst .txt,.memdumptxt,$<)" | xxd -ps -c 2 -r > "$(subst .txt,.memdump,$<)" ${Q}rm "$(subst .txt,.memdumptxt,$<)" %.run: %.txt ${SYSTEM_VVP} ${QUIET_VVP} - ${Q}vvp -i "${SYSTEM_VVP}" +BOOT_CODE="$<" + ${Q}vvp -i "${SYSTEM_VVP}" +BOOT_CODE="$<" +MICROCODE="${MICROCODE}" %.disas: %.bin objdump -D -b binary -m i8086 $^ | less diff --git a/system/Makefile b/system/Makefile index d840247..52a1092 100644 --- a/system/Makefile +++ b/system/Makefile @@ -16,10 +16,11 @@ # along with this program. If not, see . # SOURCES=processor.v testbench.v memory.v registers.v alu.v decoder.v general.v -INCLUDES=proc_state_def.v alu_header.v config.v +INCLUDES=proc_state_def.v alu_header.v config.v ucode_header.v ucode.hex SYSTEM_VVP=system.vvp BOOT_CODE=boot_code.txt GTKWSAVE=../gtkwave_savefile.gtkw +MICROCODE=ucode.hex include ../common.mk diff --git a/system/decoder.v b/system/decoder.v index 94b0429..711906b 100644 --- a/system/decoder.v +++ b/system/decoder.v @@ -19,7 +19,28 @@ `include "proc_state_def.v" `include "alu_header.v" +`include "ucode_header.v" +module microcode( + input [`UCODE_ADDR_BITS-1:0] ADDR, + output [`UCODE_DATA_BITS-1:0] DATA +); + +initial begin + string ucode_path; + if($value$plusargs("MICROCODE=%s",ucode_path))begin + $readmemb(ucode_path,ucode,0,`UCODE_SIZE-1); + end else begin + $display("Please supply microcode rom file as a runtime vvp argument +MICROCODE="); + $finish; + end +end + +reg [`UCODE_DATA_BITS-1:0] ucode [ 0:`UCODE_SIZE-1 ]; + +assign DATA=ucode[ADDR]; + +endmodule module decoder( input wire [15:0] CIR,input wire [15:0] FLAGS, output reg Wbit, output reg Sbit, output reg unaligning ,output reg opcode_size, output reg ERROR,output reg [`PROC_STATE_BITS-1:0]next_state @@ -29,6 +50,10 @@ module decoder( ,output reg [2:0]ALU_1OP ); +wire [`UCODE_DATA_BITS-1:0] ucode_data; +reg [`UCODE_ADDR_BITS-1:0] UCODE_ADDR; +microcode ucode(UCODE_ADDR,ucode_data); + /* 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 */ `define invalid_instruction next_state=`PROC_IF_STATE_ENTRY;ERROR=1;MOD=2'b11; @@ -38,94 +63,79 @@ module decoder( always @( CIR ) begin ERROR=0;HALT=0; - case(CIR[15:10]) - 6'b000001 : begin - /* ADD, ... */ - if ( CIR[9:9] == 0 )begin - /* Add Immediate word/byte to accumulator */ - /* 0 0 0 0 0 1 0 W | DATA | DATA if W |*/ - opcode_size=0; - has_operands=1; - Wbit=CIR[8:8]; - if(Wbit) - `start_unaligning_instruction - else - `start_aligning_instruction - MOD=2'b11; + casex({CIR[15:8],CIR[5:3]}) + 11'b0000_010x_xxx : begin + /* Add Immediate word/byte to accumulator */ + /* 0 0 0 0 0 1 0 W | DATA | DATA if W |*/ + opcode_size=0; + has_operands=1; + Wbit=CIR[8:8]; + if(Wbit) + `start_unaligning_instruction + else + `start_aligning_instruction + MOD=2'b11; + in_alu1_sel1=2'b00; + in_alu1_sel2=2'b01; + out_alu1_sel=3'b011; + reg_read_port1_addr={Wbit,3'b000}; + reg_write_addr={Wbit,3'b000}; + ALU_1OP=`ALU_OP_ADD; + if(Wbit==1) + next_state=`PROC_DE_LOAD_16_PARAM; + else begin + PARAM1[7:0]=CIR[7:0]; + next_state=`PROC_EX_STATE_ENTRY; + end + end + 11'b1000_00xx_000 : begin + /* Add Immediate word/byte to register/memory */ + /* 1 0 0 0 0 0 S W | MOD 0 0 0 R/M | < DISP LO > | < DISP HI > | DATA | DATA if W | */ + `start_aligning_instruction + opcode_size=1; + has_operands=1; + Wbit=CIR[8:8]; + Sbit=CIR[9:9]; + MOD=2'b11; + in_alu1_sel1=2'b00; + in_alu1_sel2=2'b01; + out_alu1_sel={1'b0,MOD}; + reg_read_port1_addr={Wbit,RM}; + reg_write_addr={Wbit,RM}; + ALU_1OP=`ALU_OP_ADD; + next_state=`PROC_DE_LOAD_16_PARAM; + if(Wbit==1) + next_state=`PROC_DE_LOAD_16_PARAM; + else begin + `invalid_instruction /*do 8bit loads*/ + end + end + 11'b1000_00xx_111 : begin + /* CMP - compare Immediate with register / memory */ + /* 1 0 0 0 0 0 S W | MOD 1 1 1 R/M | < DISP LO > | < DISP HI > | DATA | DATA if W | */ + opcode_size=1; + has_operands=1; + Wbit=CIR[8:8]; + Sbit=CIR[9:9]; + MOD=CIR[7:6]; + RM=CIR[2:0]; + if(((Wbit==1)&&(Sbit==1))||Wbit==0)begin + `start_unaligning_instruction + end else begin + `invalid_instruction; + end + if(MOD==2'b11)begin in_alu1_sel1=2'b00; in_alu1_sel2=2'b01; - out_alu1_sel=3'b011; - reg_read_port1_addr={Wbit,3'b000}; - reg_write_addr={Wbit,3'b000}; - ALU_1OP=`ALU_OP_ADD; - if(Wbit==1) - next_state=`PROC_DE_LOAD_16_PARAM; - else begin - PARAM1[7:0]=CIR[7:0]; - next_state=`PROC_EX_STATE_ENTRY; - end + reg_read_port1_addr={Wbit,RM}; + out_alu1_sel=3'b100; + ALU_1OP=`ALU_OP_SUB; + next_state=`PROC_DE_LOAD_8_PARAM; end else begin `invalid_instruction end end - 6'b100000 : begin - /* ADD, ADC, SUB, SBB, CMP , AND, ... */ - case (CIR[5:3]) - 3'b000 : begin - /* Add Immediate word/byte to register/memory */ - /* 1 0 0 0 0 0 S W | MOD 0 0 0 R/M | < DISP LO > | < DISP HI > | DATA | DATA if W | */ - `start_aligning_instruction - opcode_size=1; - has_operands=1; - Wbit=CIR[8:8]; - Sbit=CIR[9:9]; - MOD=2'b11; - in_alu1_sel1=2'b00; - in_alu1_sel2=2'b01; - out_alu1_sel={1'b0,MOD}; - reg_read_port1_addr={Wbit,RM}; - reg_write_addr={Wbit,RM}; - ALU_1OP=`ALU_OP_ADD; - next_state=`PROC_DE_LOAD_16_PARAM; - if(Wbit==1) - next_state=`PROC_DE_LOAD_16_PARAM; - else begin - `invalid_instruction /*do 8bit loads*/ - end - end - 3'b111 : begin - /* CMP - compare Immediate with register / memory */ - /* 1 0 0 0 0 0 S W | MOD 1 1 1 R/M | < DISP LO > | < DISP HI > | DATA | DATA if W | */ - opcode_size=1; - has_operands=1; - Wbit=CIR[8:8]; - Sbit=CIR[9:9]; - MOD=CIR[7:6]; - RM=CIR[2:0]; - if(((Wbit==1)&&(Sbit==1))||Wbit==0)begin - `start_unaligning_instruction - end else begin - `invalid_instruction; - end - if(MOD==2'b11)begin - in_alu1_sel1=2'b00; - in_alu1_sel2=2'b01; - reg_read_port1_addr={Wbit,RM}; - out_alu1_sel=3'b100; - ALU_1OP=`ALU_OP_SUB; - next_state=`PROC_DE_LOAD_8_PARAM; - end else begin - `invalid_instruction - end - - end - default:begin - `invalid_instruction - end - endcase - end - 6'b101100, - 6'b101101:begin + 11'b1011_0xxx_xxx : begin /* MOV - Move Immediate byte to register */ /* 1 0 1 1 W REG | DATA | DATA if W |*/ `start_aligning_instruction @@ -142,8 +152,7 @@ always @( CIR ) begin ALU_1OP=`ALU_OP_ADD; next_state=`PROC_EX_STATE_ENTRY; end - 6'b101110, - 6'b101111 : begin + 11'b1011_1xxx_xxx : begin /*MOV - Move Immediate word to register*/ `start_unaligning_instruction has_operands=1; @@ -159,7 +168,7 @@ always @( CIR ) begin next_state=`PROC_DE_LOAD_16_PARAM; end - 6'b100010 : begin + 11'b1000_10xx_xxx : begin /* MOV - Reg/Mem to/from register */ /* 1 0 0 0 1 0 D W | MOD REG RM | < DISP LO > | < DISP HI > |*/ has_operands=0; @@ -204,10 +213,7 @@ always @( CIR ) begin ALU_1OP=`ALU_OP_ADD; PARAM2=0; end - 6'b010000,//INC - 6'b010001,//INC - 6'b010010,//DEC - 6'b010011:begin//DEC + 11'b0100_xxxx_xxx:begin//DEC /* DEC - Decrement Register */ /* | 0 1 0 0 1 REG | */ /* INC - Increment Register */ @@ -229,97 +235,69 @@ always @( CIR ) begin ALU_1OP=`ALU_OP_SUB; next_state=`PROC_EX_STATE_ENTRY; end - 6'b111111 : begin - /* INC */ - if (CIR[9:9] == 1 ) begin - case (CIR[5:3]) - 3'b000,3'b001 :begin - /* INC - Register/Memory */ - /* 1 1 1 1 1 1 1 W | MOD 0 0 0 R/M | < DISP LO> | < DISP HI> */ - /* DEC - Register/Memory */ - /* 1 1 1 1 1 1 1 W | MOD 0 0 1 R/M | < DISP LO> | < DISP HI> */ - has_operands=1; - opcode_size=1; - `start_aligning_instruction - Wbit=CIR[8:8]; - MOD=CIR[7:6]; - RM=CIR[2:0]; - in_alu1_sel1=(MOD==2'b11)? 2'b01 : 2'b00; - in_alu1_sel2=2'b00;/* number 1 */ - PARAM2=1; - out_alu1_sel={1'b0,MOD}; + 11'b1111_111x_00x : begin + /* INC - Register/Memory */ + /* 1 1 1 1 1 1 1 W | MOD 0 0 0 R/M | < DISP LO> | < DISP HI> */ + /* DEC - Register/Memory */ + /* 1 1 1 1 1 1 1 W | MOD 0 0 1 R/M | < DISP LO> | < DISP HI> */ + has_operands=1; + opcode_size=1; + `start_aligning_instruction + Wbit=CIR[8:8]; + MOD=CIR[7:6]; + RM=CIR[2:0]; + in_alu1_sel1=(MOD==2'b11)? 2'b01 : 2'b00; + in_alu1_sel2=2'b00;/* number 1 */ + PARAM2=1; + out_alu1_sel={1'b0,MOD}; - /*in case MOD=11 */ - reg_read_port1_addr={1'b0,RM}; - reg_write_addr={1'b0,RM}; + /*in case MOD=11 */ + reg_read_port1_addr={1'b0,RM}; + reg_write_addr={1'b0,RM}; - ALU_1OP=(CIR[3:3]==1)?`ALU_OP_SUB:`ALU_OP_ADD; - if ( MOD == 2'b11 ) - next_state=`PROC_EX_STATE_ENTRY; - else - next_state=`RPOC_MEMIO_READ; - end - default:begin - `invalid_instruction - end - endcase - end else begin - `invalid_instruction + ALU_1OP=(CIR[3:3]==1)?`ALU_OP_SUB:`ALU_OP_ADD; + if ( MOD == 2'b11 ) + next_state=`PROC_EX_STATE_ENTRY; + else + next_state=`RPOC_MEMIO_READ; + end + 11'b1111_0100_xxx : begin + /* HLT - Halt */ + /* 1 1 1 1 0 1 0 0 | */ + has_operands=0; + opcode_size=0; + `start_unaligning_instruction + MOD=2'b11; + HALT=1; + next_state=`PROC_HALT_STATE; + end + 11'b0011_110x_xxx : begin + /* CMP - Compare Immediate with accumulator */ + /* 0 0 1 1 1 1 0 W | DATA | DATA if W |*/ + /* */ + /* NOTE: 8086 doc doesn't show the third byte but the */ + /* W flag and my assembler seem to disagree */ + Wbit=CIR[8:8]; + opcode_size=0; + has_operands=1; + if(Wbit) + `start_unaligning_instruction + else + `start_aligning_instruction + MOD=2'b11; + in_alu1_sel1=2'b00; + in_alu1_sel2=2'b01; + reg_read_port1_addr={Wbit,3'b000}; + out_alu1_sel=3'b100; + ALU_1OP=`ALU_OP_SUB; + if(Wbit==1) + next_state=`PROC_DE_LOAD_16_PARAM; + else begin + PARAM1[7:0]=CIR[7:0]; + next_state=`PROC_EX_STATE_ENTRY; end end - 6'b111101 : begin - /*HLT, CMC, TEST, NOT, NEG, MUL, IMUL, .... */ - case (CIR[9:8]) - 2'b00:begin - /* HLT - Halt */ - /* 1 1 1 1 0 1 0 0 | */ - has_operands=0; - opcode_size=0; - `start_unaligning_instruction - MOD=2'b11; - HALT=1; - next_state=`PROC_HALT_STATE; - end - default:begin - `invalid_instruction; - end - endcase - - end - 6'b001111 : begin - if ( CIR[9:9] == 0 ) begin - /* CMP - Compare Immediate with accumulator */ - /* 0 0 1 1 1 1 0 W | DATA | DATA if W |*/ - /* */ - /* NOTE: 8086 doc doesn't show the third byte but the */ - /* W flag and my assembler seem to disagree */ - Wbit=CIR[8:8]; - opcode_size=0; - has_operands=1; - if(Wbit) - `start_unaligning_instruction - else - `start_aligning_instruction - MOD=2'b11; - in_alu1_sel1=2'b00; - in_alu1_sel2=2'b01; - reg_read_port1_addr={Wbit,3'b000}; - out_alu1_sel=3'b100; - ALU_1OP=`ALU_OP_SUB; - if(Wbit==1) - next_state=`PROC_DE_LOAD_16_PARAM; - else begin - PARAM1[7:0]=CIR[7:0]; - next_state=`PROC_EX_STATE_ENTRY; - end - end else begin - `invalid_instruction - end - end - 6'b011100, - 6'b011101, - 6'b011110, - 6'b011111:begin + 11'b0111_xxxx_xxx:begin /* Conditional relative jumps */ /* JE/JZ - Jump on Zero */ /* 0 1 1 1 0 1 0 0 | IP-INC8 |*/ @@ -372,68 +350,31 @@ always @( CIR ) begin end endcase end - 6'b111010:begin - /* JMP,CALL */ - case(CIR[9:8]) - 2'b00: begin - /* CALL - Call direct within segment */ - /* 1 1 1 0 1 0 0 0 | IP-INC-LO | IP-INC-HI |*/ - `invalid_instruction - end - 2'b01: begin - /* JMP - Unconditional Jump direct within segment */ - /* 1 1 1 0 1 0 0 1 | IP-INC-LO | IP-INC-HI |*/ - `invalid_instruction - end - 2'b10: begin - /* JMP - Unconditional jump direct intersegment */ - /* 0 0 0 0 0 0 0 0 | IP-LO | IP-HI | CS-LO | CS-HI | */ - `invalid_instruction - end - 2'b11: begin - /* JMP - Unconditional jump direct within segment (short) */ - /* | 1 1 1 0 1 0 0 1 | IP-INC-LO | */ - `start_aligning_instruction - opcode_size=0; - has_operands=1; - Wbit=1; - in_alu1_sel1=2'b10; - in_alu1_sel2=2'b00; - PARAM2={{8{CIR[7:7]}},CIR[7:0]}; - ALU_1OP=`ALU_OP_ADD_SIGNED_B; - out_alu1_sel=3'b101; - next_state=`PROC_EX_STATE_ENTRY; - end - endcase + 11'b1110_1011_xxx:begin + /* JMP - Unconditional jump direct within segment (short) */ + /* | 1 1 1 0 1 0 1 1 | IP-INC-LO | */ + `start_aligning_instruction + opcode_size=0; + has_operands=1; + Wbit=1; + in_alu1_sel1=2'b10; + in_alu1_sel2=2'b00; + PARAM2={{8{CIR[7:7]}},CIR[7:0]}; + ALU_1OP=`ALU_OP_ADD_SIGNED_B; + out_alu1_sel=3'b101; + next_state=`PROC_EX_STATE_ENTRY; end - 6'b110011:begin - case(CIR[9:8]) - 2'b00:begin - `invalid_instruction - end - 2'b01:begin - if(CIR[7:0]==8'h21) begin - /* INT - execute interrupt handler */ - /* 1 1 0 0 1 1 0 1 | DATA |*/ - has_operands=1; - opcode_size=0; - `start_aligning_instruction - /* Emulate MS-DOS print routines */ - if(register_file.registers[0][15:8]==8'h02)begin - $write("%s" ,register_file.registers[2][7:0]); /*TODO:Could trigger erroneously while CIR is not final*/ - end - next_state=`PROC_IF_STATE_ENTRY; - end else begin - `invalid_instruction - end - end - 2'b10:begin - `invalid_instruction - end - 2'b11:begin - `invalid_instruction - end - endcase + 11'b1100_1101_xxx:begin + /* INT - execute interrupt handler */ + /* 1 1 0 0 1 1 0 1 | DATA |*/ + has_operands=1; + opcode_size=0; + `start_aligning_instruction + /* Emulate MS-DOS print routines */ + if(CIR[7:0]==8'h21 && register_file.registers[0][15:8]==8'h02)begin + $write("%s" ,register_file.registers[2][7:0]); /*TODO:Could trigger erroneously while CIR is not final*/ + end + next_state=`PROC_IF_STATE_ENTRY; end default:begin `invalid_instruction diff --git a/system/ucode.hex b/system/ucode.hex new file mode 100644 index 0000000..1609230 --- /dev/null +++ b/system/ucode.hex @@ -0,0 +1,9 @@ + +//s : Opcode size 0:8bit 1:16bit +//h : Has operands as part of the instruction: 0: No 1: Yes + +// |sh +@000 01_11 +@001 11_10 +@002 1000 +@003 0010 diff --git a/system/ucode_header.v b/system/ucode_header.v new file mode 100644 index 0000000..83a9b3a --- /dev/null +++ b/system/ucode_header.v @@ -0,0 +1,6 @@ +`define UCODE_ADDR_BITS 9 +`define UCODE_DATA_BITS 32 +`define UCODE_SIZE 4 + +`define ADD_AL_IB 0 +`define ADD_AX_IW 1