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